1   package com.trendmicro.grid.acl.metadata;
2   
3   import com.trendmicro.grid.acl.commons.XmlSerializer;
4   import com.trendmicro.grid.acl.commons.collections.AbstractMapValueCollection;
5   
6   import javax.xml.bind.annotation.*;
7   import java.io.*;
8   import java.util.Collection;
9   import java.util.List;
10  import java.util.Map;
11  
12  /**
13   * Defines the metadata root element used to store a single metadata package.
14   *
15   * @author Juergen_Kellerer, 2010-04-19
16   * @version 1.0
17   */
18  @XmlAccessorType(XmlAccessType.PROPERTY)
19  @XmlType(name = "metadata", namespace = Metadata.NS)
20  @XmlRootElement(name = "metadata", namespace = Metadata.NS)
21  public class Metadata implements Cloneable, Externalizable {
22  
23  	private static class MetaCollectionMapAdapter extends AbstractMapValueCollection<String, Meta> {
24  		private MetaCollectionMapAdapter(Map<String, Meta> wrappedMap) {
25  			super(wrappedMap);
26  		}
27  
28  		protected String createKeyForValue(Meta value) {
29  			return value.getName();
30  		}
31  	}
32  
33  	private static final long serialVersionUID = 8942843714209700817L;
34  	private static final byte serialVersion = 1;
35  
36  	public static final String NS = "http://grid.trendmicro.com/metadata/1.0";
37  	public static final String DEFAULT_SCHEMA_BASENAME = "metadata-schema";
38  
39  	private static XmlSerializer<Metadata> xmlSerializer =
40  			new XmlSerializer<Metadata>(Metadata.class, NS, DEFAULT_SCHEMA_BASENAME);
41  
42  	public static XmlSerializer<Metadata> getXmlSerializer() {
43  		return xmlSerializer;
44  	}
45  
46  	public static List<File> generateSchema(final File outputDirectory, final String basename) throws Exception {
47  		return xmlSerializer.generateSchema(outputDirectory, basename);
48  	}
49  
50  	public static Metadata load(File file) throws Exception {
51  		return xmlSerializer.load(file);
52  	}
53  
54  	public static Metadata load(InputStream in) throws Exception {
55  		return xmlSerializer.load(in);
56  	}
57  
58  	public void save(File file) throws Exception {
59  		xmlSerializer.save(this, file);
60  	}
61  
62  	public void save(OutputStream out) throws Exception {
63  		xmlSerializer.save(this, out);
64  	}
65  
66  	ValidatedMetaMap metaElements;
67  	final transient Collection<Meta> valueCollection;
68  
69  	/**
70  	 * Constructs a new, empty Metadata instance.
71  	 */
72  	public Metadata() {
73  		metaElements = new ValidatedMetaMap();
74  		valueCollection = new MetaCollectionMapAdapter(metaElements);
75  	}
76  
77  	/**
78  	 * Constructs a copied Metadata instance.
79  	 *
80  	 * @param other The Metadata instance to copy.
81  	 */
82  	@SuppressWarnings("unchecked")
83  	public Metadata(Metadata other) {
84  		metaElements = other.metaElements.clone();
85  		for (Map.Entry<String, Meta> entry : metaElements.entrySet())
86  			entry.setValue(entry.getValue().clone());
87  		valueCollection = new MetaCollectionMapAdapter(metaElements);
88  	}
89  
90  	/**
91  	 * Constructs a new, Metadata instance that is filled with the given elements.
92  	 *
93  	 * @param metaElements the elements to place inside this instance.
94  	 */
95  	public Metadata(Collection<Meta> metaElements) {
96  		this();
97  		setMetaElements(metaElements);
98  	}
99  
100 	/**
101 	 * Returns all elements.
102 	 *
103 	 * @return all elements.
104 	 */
105 	@XmlElement(name = "meta", namespace = NS)
106 	public Collection<Meta> getMetaElements() {
107 		return valueCollection;
108 	}
109 
110 	public void setMetaElements(Collection<Meta> metaElements) {
111 		if (metaElements != valueCollection) {
112 			this.metaElements.clear();
113 
114 			if (metaElements != null)
115 				for (Meta meta : metaElements)
116 					this.metaElements.put(meta.getName(), meta);
117 		}
118 	}
119 
120 	/**
121 	 * Validates that all contained values are valid and writable in the current context.
122 	 */
123 	public void assertValuesAreValidForWrite() {
124 		metaElements.assertValuesAreValidForWrite();
125 	}
126 
127 	/**
128 	 * Returns a map of all Meta elements.
129 	 *
130 	 * @return a map of all Meta elements.
131 	 */
132 	public Map<String, Meta> getAll() {
133 		return metaElements;
134 	}
135 
136 	/**
137 	 * Adds or overwrites a new Meta element.
138 	 *
139 	 * @param name the name of the Meta element to add or overwrite.
140 	 * @return The newly added Meta element.
141 	 */
142 	public Meta add(String name) {
143 		Meta p = new Meta(name);
144 		metaElements.put(name, p);
145 		return p;
146 	}
147 
148 	/**
149 	 * Returns the named meta element or 'null' if not existing.
150 	 *
151 	 * @param name the name of the Meta element to return.
152 	 * @return the named meta element or 'null' if not existing.
153 	 */
154 	public Meta get(final String name) {
155 		return get(name, null);
156 	}
157 
158 	/**
159 	 * Returns the named meta element or 'defaultValue' if not existing.
160 	 *
161 	 * @param name		 the name of the Meta element to return.
162 	 * @param defaultValue the Meta element to return if the named element does not exist.
163 	 * @return the named meta element or 'defaultValue' if not existing.
164 	 */
165 	public Meta get(final String name, final Meta defaultValue) {
166 		Meta meta = metaElements.get(name);
167 		return meta != null ? meta : defaultValue;
168 	}
169 
170 	/**
171 	 * Adds a new Meta element if not existing or returns the existing element.
172 	 *
173 	 * @param name the name of the Meta element to return or create.
174 	 * @return a new Meta element if not existing or returns the existing element.
175 	 */
176 	public Meta addOrGet(final String name) {
177 		Meta meta = get(name);
178 		return meta != null ? meta : add(name);
179 	}
180 
181 	/**
182 	 * Removes the named Meta element and returns it.
183 	 *
184 	 * @param name the name of the Meta element to return or create.
185 	 * @return the removed Meta element or 'null' if it didn't exist.
186 	 */
187 	public Meta remove(final String name) {
188 		return metaElements.remove(name);
189 	}
190 
191 	/**
192 	 * {@inheritDoc}
193 	 */
194 	@Override
195 	public String toString() {
196 		return "Metadata{" +
197 				"metaElements=" + valueCollection +
198 				'}';
199 	}
200 
201 	/**
202 	 * {@inheritDoc}
203 	 */
204 	@Override
205 	public Metadata clone() {
206 		return new Metadata(this);
207 	}
208 
209 	/**
210 	 * {@inheritDoc}
211 	 */
212 	@Override
213 	public boolean equals(Object o) {
214 		if (this == o) return true;
215 		if (!(o instanceof Metadata)) return false;
216 		Metadata metadata = (Metadata) o;
217 		if (valueCollection.size() == metadata.valueCollection.size()) {
218 			// Start comparisons with value collection as this is faster, fallback to map compare.
219 			return valueCollection.equals(metadata.valueCollection) ||
220 					metaElements.equals(metadata.metaElements);
221 		} else
222 			return false;
223 	}
224 
225 	/**
226 	 * {@inheritDoc}
227 	 */
228 	@Override
229 	public int hashCode() {
230 		return metaElements.hashCode();
231 	}
232 
233 	/**
234 	 * {@inheritDoc}
235 	 */
236 	@Override
237 	public void writeExternal(ObjectOutput out) throws IOException {
238 		out.writeByte(serialVersion);
239 
240 		final Collection<Meta> metaCollection = metaElements.valuesWithoutValidation();
241 		out.writeInt(metaCollection.size());
242 		for (Meta meta : metaCollection)
243 			meta.writeExternal(out);
244 	}
245 
246 	/**
247 	 * {@inheritDoc}
248 	 */
249 	@Override
250 	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
251 		switch (in.readByte()) {
252 			case 1:
253 				if (!metaElements.isEmpty())
254 					metaElements.clear();
255 
256 				for (int i = in.readInt(); i > 0; i--) {
257 					Meta meta = new Meta();
258 					meta.readExternal(in);
259 					metaElements.putWithoutValidation(meta);
260 				}
261 				break;
262 			default:
263 				throw new IOException("Stream is corrupted or was written with a newer unsupported version.");
264 		}
265 	}
266 }