001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010:
011: package org.mmbase.datatypes;
012:
013: import java.util.*;
014: import org.mmbase.datatypes.util.xml.DataTypeDefinition;
015: import org.mmbase.util.logging.*;
016:
017: /**
018: * A DataTypeCollector is a collection of named DataTypes. So, you can add and request DataType
019: * objects from this by a String. It also facilitates 'chaining' because you can add other
020: * DataTypeCollectors to it. It will delegate searching of a datatype to them, if a certain key is
021: * not available.
022: * <br />
023: * This object also knowns how to 'lock' its DataType's using it's 'signature'. I have no idea where
024: * that is good for.
025: *
026: * @author Pierre van Rooden
027: * @since MMBase-1.8
028: * @version $Id: DataTypeCollector.java,v 1.15 2008/02/16 22:13:53 nklasens Exp $
029: */
030:
031: public final class DataTypeCollector {
032:
033: private static final Logger log = Logging
034: .getLoggerInstance(DataTypeCollector.class);
035:
036: // Map of datatypes local to this collector
037: private Map<String, BasicDataType<?>> dataTypes = new HashMap<String, BasicDataType<?>>(); // String -> BasicDataType
038: private Map<String, Set<DataType<?>>> specializations = new HashMap<String, Set<DataType<?>>>(); // String -> Set
039: private Set<DataType<?>> roots = new HashSet<DataType<?>>(); // All datatypes which did't inherit from another datatype (this should normally be (a subset of) the 'database types' of mmbase)
040:
041: // the object to finish datatypes with
042: private Object signature = null;
043:
044: // dependent collectors
045: private List<DataTypeCollector> collectors = new ArrayList<DataTypeCollector>();
046:
047: // the DataTypeCollector used to store datatypes accessible throughout the application
048: private static DataTypeCollector systemDataTypeCollector;
049:
050: /**
051: * Creates the DataTypeCollector used to store datatypes accessible throughout the application.
052: * Called by the {@link DataTypes} class.
053: */
054: static DataTypeCollector createSystemDataTypeCollector() {
055: if (systemDataTypeCollector == null) {
056: Object signature = new String("SYSTEM_"
057: + System.currentTimeMillis());
058: systemDataTypeCollector = new DataTypeCollector(signature);
059: return systemDataTypeCollector;
060: } else {
061: throw new SecurityException(
062: "System datatype collector already defined, may not be created twice.");
063: }
064: }
065:
066: /**
067: * Constructor.
068: * @param signature the object used to finish a data type for this collector.
069: */
070: public DataTypeCollector(Object signature) {
071: this .signature = signature;
072: }
073:
074: public DataTypeDefinition getDataTypeDefinition() {
075: return new DataTypeDefinition(this );
076: }
077:
078: /**
079: * Adds a datatype collector on which this collector depends.
080: * when trying to obtain a datatype or datatype instance, if
081: * the current collector does not contain the datatype, it tries to obtain
082: * it from any colelctors it depends on.
083: * @param collector the dataType collector to add
084: */
085: public void addCollector(DataTypeCollector collector) {
086: collectors.add(collector);
087: }
088:
089: /**
090: * Set local datatypes of the collector
091: * @param dataTypes a <code>Map</code> containing the datatypes
092: */
093: public void setDataTypes(Map<String, BasicDataType<?>> dataTypes) {
094: this .dataTypes = dataTypes;
095: if (log.isDebugEnabled())
096: log.debug("DataTypes for collector with signature "
097: + signature + ":" + dataTypes);
098: }
099:
100: /**
101: * Set local datatypes of the collector
102: */
103: public Map<String, BasicDataType<?>> getDataTypes() {
104: return dataTypes;
105: }
106:
107: /**
108: * Get a datatype defined for this collector.
109: * @param name the name of the datatype to retrieve
110: * @return a {@link DataType} with the given name, as defined for this collector, or <code>null</code>
111: * if no datatype is defined.
112: */
113: public BasicDataType<?> getDataType(String name) {
114: return getDataType(name, false);
115: }
116:
117: /**
118: * Adds a datatype to this collector.
119: * The datatype should have a unique id. If it has no id (i.e. getName() returns an empty string), it is not added.
120: * If the datatype overrides an existing datatype, a warning is logged.
121: * @param dataType the datatype to add
122: * @return if applicable, the old (original) datatype with the same id as the dattype that was being added, <code>null</code>
123: * if it is not applicable.
124: */
125: public BasicDataType<?> addDataType(BasicDataType<?> dataType) {
126: String name = dataType.getName();
127: if (name == null || "".equals(name)) {
128: // not a proper id, so do not add
129: return null;
130: } else {
131: BasicDataType<?> origin = dataType.getOrigin();
132: if (origin != null) {
133: if (origin.equals(getDataType(origin.getName()))) { // origin is also in this collector
134: Set<DataType<?>> spec = specializations.get(origin
135: .getName());
136: // TODO, not sure that this stuff with specializations goes ok when using 'parent' collectors.
137: // Does not matter very much, because you problably want to use this functionlaity mainly on the System Collector
138: if (spec == null) {
139: spec = new HashSet<DataType<?>>();
140: specializations.put(origin.getName(), spec);
141: }
142: spec.add(dataType);
143: } else {
144: roots.add(dataType);
145: }
146: } else {
147: roots.add(dataType);
148: }
149: BasicDataType<?> old = dataTypes.put(name, dataType);
150: if (old != null && old != dataType) {
151: log.warn("Replaced " + name + " " + old + " with "
152: + dataType);
153: }
154: return old;
155: }
156: }
157:
158: /**
159: * Returns a set of all DataTypes in this collector which are directly inherited from the one with given name
160: */
161: public Collection<DataType<?>> getSpecializations(String name) {
162: // TODO: see in addDataType
163: Set<DataType<?>> set = specializations.get(name);
164: if (set == null) {
165: return Collections.emptySet();
166: } else {
167: return Collections.unmodifiableSet(set);
168: }
169: }
170:
171: /**
172: * Recursively calls {@link #getSpecializations(String)} so that you can easily iterate also all indirectly specializaed versions of a certain DataType in this collector
173: */
174: public Iterator<DataType<?>> getAllSpecializations(String name) {
175: final Iterator<DataType<?>> i = getSpecializations(name)
176: .iterator();
177: return new Iterator<DataType<?>>() {
178: DataType<?> next = i.hasNext() ? i.next() : null;
179: Iterator<DataType<?>> subIterator = null;
180:
181: public boolean hasNext() {
182: return next != null || subIterator != null;
183: }
184:
185: public DataType<?> next() {
186: if (subIterator != null) {
187: DataType<?> n = subIterator.next();
188: if (!subIterator.hasNext())
189: subIterator = null;
190: return n;
191: }
192: if (next != null) {
193: subIterator = getAllSpecializations(next.getName());
194: if (!subIterator.hasNext())
195: subIterator = null;
196: DataType<?> n = next;
197: if (i.hasNext()) {
198: next = i.next();
199: } else {
200: next = null;
201: }
202: return n;
203: }
204: throw new NoSuchElementException();
205: }
206:
207: public void remove() {
208: throw new UnsupportedOperationException();
209: }
210: };
211: }
212:
213: /**
214: * Returns all DataTypes in this Collector which did not have an origina DataType (in this Collector).
215: */
216: public Set<DataType<?>> getRoots() {
217: // TODO: see in addDataType
218: return Collections.unmodifiableSet(roots);
219: }
220:
221: /**
222: * Get a datatype defined for this collector, and possibly any collectors it depends on.
223: * The collector first searches among datatypes defined in its own set.
224: * If that fails, it tries ot get it from any of the other collectors it may depend on, and eventually
225: * from the main repository.
226: * @param name the name of the datatype to retrieve
227: * @param recursive if <code>true</code>, the datatype is also searched in collectors it depends on.
228: * @return a {@link DataType} with the given name, as defined for this collector, or <code>null</code>
229: * if no datatype is defined.
230: */
231: public BasicDataType<?> getDataType(String name, boolean recursive) {
232: BasicDataType<?> dataType = dataTypes.get(name);
233: if (this != systemDataTypeCollector && dataType == null
234: && recursive) {
235: for (Iterator<DataTypeCollector> i = collectors.iterator(); dataType == null
236: && i.hasNext();) {
237: DataTypeCollector collector = i.next();
238: dataType = collector.getDataType(name, true);
239: }
240: if (dataType == null) {
241: dataType = systemDataTypeCollector.getDataType(name,
242: false);
243: }
244: }
245: return dataType;
246: }
247:
248: /**
249: * Get a datatype instance through this collector.
250: * The collector first searches among datatypes defined in its own set.
251: * If that fails, it tries to get it from any of the other collectors it may depend on, and eventually
252: * from the main repository.
253: * If that fails too, it creates one itself based on the passed base datatype (if given).
254: * @param name the name of the datatype to retrieve
255: * @param baseDataType the datatype to base a new datatype on if it is not yet defined. Can be <code>null</code>.
256: * @return a {@link DataType} with the given name, as defined for this collector, or <code>null</code>
257: * if no datatype is defined and no base datatype was passed.
258: */
259: public BasicDataType<?> getDataTypeInstance(String name,
260: DataType<?> baseDataType) {
261: BasicDataType<?> dataType = getDataType(name, true);
262: if (dataType == null && baseDataType == null) {
263: return null;
264: } else if (dataType == null) {
265: return (BasicDataType<?>) baseDataType.clone(name);
266: } else {
267: return (BasicDataType<?>) dataType.clone();
268: }
269: }
270:
271: /**
272: * Returns whether the dataType with the given name is part of the current collection.
273: */
274: public boolean contains(String name) {
275: return dataTypes.containsKey(name);
276: }
277:
278: /**
279: * Returns whether the dataType is part of the current collection.
280: */
281: public boolean contains(DataType<?> dataType) {
282: return dataTypes.containsValue(dataType);
283: }
284:
285: /**
286: * Unlock a dataType so it can be changed or altered.
287: * This will likely fail if the datatype is not part of this collector.
288: */
289: public void rewrite(DataType<?> dataType) {
290: dataType.rewrite(signature);
291: }
292:
293: /**
294: * Lock a dataType so it can be changed or altered.
295: * This will likely fail if the datatype is not part of this collector.
296: */
297: public void finish(DataType<?> dataType) {
298: dataType.finish(signature);
299: }
300:
301: public String toString() {
302: return signature + ": " + dataTypes.values() + " " + collectors;
303: }
304:
305: }
|