KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > datatypes > util > xml > DataTypeDefinition


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.datatypes.util.xml;
11
12 import java.util.*;
13 import org.w3c.dom.*;
14 import java.lang.reflect.Method JavaDoc;
15 import java.lang.reflect.InvocationTargetException JavaDoc;
16
17 import org.mmbase.bridge.Field;
18 import org.mmbase.datatypes.processors.*;
19 import org.mmbase.bridge.util.Queries;
20 import org.mmbase.datatypes.*;
21 import org.mmbase.core.util.Fields;
22 import org.mmbase.util.*;
23 import org.mmbase.util.functions.Parameters;
24 import org.mmbase.util.xml.DocumentReader;
25 import org.mmbase.util.xml.XMLWriter;
26 import org.mmbase.util.logging.*;
27 import org.mmbase.util.transformers.*;
28
29 /**
30  * This utility class contains methods to instantiate the right DataType instance. It is used by DataTypeReader.
31  *
32  * XXX MM: This class does the actual datatype reading, so more correctly this one would be called
33  * DatatypeReader?
34  *
35  * @author Pierre van Rooden
36  * @author Michiel Meeuwissen
37  * @version $Id: DataTypeDefinition.java,v 1.55 2006/04/10 15:15:01 michiel Exp $
38  * @since MMBase-1.8
39  **/

40 public class DataTypeDefinition {
41
42     private static final Logger log = Logging.getLoggerInstance(DataTypeDefinition.class);
43
44     /**
45      * The data type which will be produced
46      */

47     public BasicDataType dataType = null;
48
49     /**
50      * The base data type on which it was based, or <code>null</code>
51      */

52     private BasicDataType baseDataType = null;
53
54     /**
55      * The data type collector that contains the data datatype with this definition.
56      */

57     protected final DataTypeCollector collector;
58
59     /**
60      * Constructor.
61      */

62     public DataTypeDefinition(DataTypeCollector collector) {
63         this.collector = collector;
64     }
65
66     /**
67      * If id was empty string, then this can still be equal to baseDataType, and nothing changed. Never <code>null</code>
68      * @param dataTypeElement piece of XML used to configure. Only the 'class' subelements are explored in this method.
69      * @param id the new id or empty string (which means that is still identical to baseDataType)
70      *
71      */

72     private void getImplementation(Element dataTypeElement, String JavaDoc id) {
73         BasicDataType dt = id.equals("") ? null : collector.getDataType(id);
74         if (dt != null) {
75             collector.rewrite(dt);
76         }
77
78         // Check the class element only.
79
NodeList childNodes = dataTypeElement.getChildNodes();
80         for (int i = 0; i < childNodes.getLength(); i++) {
81             if (childNodes.item(i) instanceof Element) {
82                 Element childElement = (Element) childNodes.item(i);
83                 if (childElement.getLocalName().equals("class")) {
84                     String JavaDoc className = childElement.getAttribute("name");
85                     if (dt != null) {
86                         log.debug("Already defined " + id);
87                         if (! className.equals(dt.getClass().getName())) {
88                             log.error("Cannot change class for '" + id + "' from " + dt.getClass().getName() + " to '" + className + "'");
89                         }
90                     } else {
91                         try {
92                             Class JavaDoc claz = Class.forName(className);
93                             log.debug("Instantiating " + claz + " for " + dataType);
94                             java.lang.reflect.Constructor JavaDoc constructor = claz.getConstructor(new Class JavaDoc[] { String JavaDoc.class});
95                             dt = (BasicDataType) constructor.newInstance(new Object JavaDoc[] { id });
96                             if (baseDataType != null) {
97                                 // should check class here, perhaps
98
dt.inherit((BasicDataType) baseDataType);
99                             }
100                         } catch (Exception JavaDoc e) {
101                             log.error(e);
102                         }
103                     }
104                     break;
105                 } else {
106                     continue;
107                 }
108             }
109         }
110         if (dt == null) { // either it had no id, or it did not exist yet.
111
if (baseDataType == null) {
112                 log.warn("No base datatype available and no class specified for datatype '" + id + "', using 'unknown' for know.\n" + XMLWriter.write(dataTypeElement, true, true));
113                 baseDataType = Constants.DATATYPE_UNKNOWN;
114             }
115             dataType = (BasicDataType) baseDataType.clone(id);
116         } else { // means that it existed it already
117
log.debug("Existing datatype " + dt + " with base " + baseDataType);
118             dataType = dt;
119         }
120
121     }
122
123     /**
124      * Configures the data type definition, using data from a DOM element
125      */

126     DataTypeDefinition configure(Element dataTypeElement, BasicDataType requestBaseDataType) {
127
128         String JavaDoc id = DataTypeXml.getAttribute(dataTypeElement, "id");
129
130         String JavaDoc base = DataTypeXml.getAttribute(dataTypeElement, "base");
131
132         if (log.isDebugEnabled()) {
133             log.debug("Reading element id='" + id + "' base='" + base + "' req datatype " + requestBaseDataType);
134         }
135         if (! base.equals("")) { // also specified, let's see if it is correct
136

137             BasicDataType definedBaseDataType = collector.getDataType(base, true);
138             if (requestBaseDataType != null) {
139                 if (requestBaseDataType != definedBaseDataType) {
140                     if ("".equals(id)) {
141                         // in builder you often 'anonymously' override or define datatype.
142
// don't pollute log with warning if e.g. using datetime datatype on integer. That is supported. Though some features may perish.
143
log.debug("Inheriting a " + definedBaseDataType + " from " + requestBaseDataType + ", functionality may get lost");
144                     } else {
145                         log.warn("Attribute 'base' ('" + base+ "') not allowed with datatype '" + id + "', because it has already an baseDataType '" + definedBaseDataType + "' in " + XMLWriter.write(dataTypeElement, true, true) + " of " + XMLWriter.write(dataTypeElement.getParentNode(), true, true));
146                     }
147                     definedBaseDataType = requestBaseDataType; // requestedBaseDataType takes precedence!
148
}
149             }
150
151             if (definedBaseDataType == null) {
152                 log.warn("Attribute 'base' ('" + base + "') of datatype '" + id + "' is an unknown datatype.");
153             } else {
154                 requestBaseDataType = definedBaseDataType;
155             }
156         }
157
158         baseDataType = requestBaseDataType;
159         getImplementation(dataTypeElement, id);
160         LocalizedString description = dataType.getLocalizedDescription();
161         DataTypeXml.getLocalizedDescription("description", dataTypeElement, description, dataType.getName());
162         configureConditions(dataTypeElement, id);
163
164         return this;
165     }
166
167     private static final java.util.regex.Pattern JavaDoc nonConditions = java.util.regex.Pattern.compile("specialization|datatype|class|description");
168
169     /**
170      * Configures the conditions of a datatype definition, using data from a DOM element
171      */

172     protected void configureConditions(Element dataTypeElement, String JavaDoc id) {
173         log.debug("Now going to configure " + dataType);
174         // add conditions
175
NodeList childNodes = dataTypeElement.getChildNodes();
176         for (int k = 0; k < childNodes.getLength(); k++) {
177             if (childNodes.item(k) instanceof Element) {
178                 Element childElement = (Element) childNodes.item(k);
179                 if (childElement.getLocalName().equals("")) {
180                     continue;
181                 }
182                 if (nonConditions.matcher(childElement.getLocalName()).matches()) {
183                     continue;
184                 }
185                 log.debug("Considering " + childElement.getLocalName() + " for " + dataType);
186                 if (!addCondition(childElement)) {
187                     log.error("" + XMLWriter.write(childElement, true, true) + " defines '" + childElement.getLocalName() + "', but " + dataType + " doesn't support that");
188                 }
189             }
190         }
191     }
192
193     /**
194      * Uses one subelement of a datatype xml configuration element and interpret it. Possibly this
195      * method is a good candidate to override.
196      * @return whether successfully read the element.
197      */

198     protected boolean addCondition(Element childElement) {
199         boolean ret = false;
200         String JavaDoc childTag = childElement.getLocalName();
201         if ("property".equals(childTag)) {
202             ret = setProperty(childElement);
203         } else if ("required".equals(childTag)) {
204             boolean value = DataTypeXml.getBooleanValue(childElement, false);
205             dataType.setRequired(value);
206             setRestrictionData(dataType.getRequiredRestriction(), childElement);
207             ret = true;
208         } else if ("unique".equals(childTag)) {
209             boolean value = DataTypeXml.getBooleanValue(childElement, false);
210             dataType.setUnique(value);
211             setRestrictionData(dataType.getUniqueRestriction(), childElement);
212             ret = true;
213         } else if ("getprocessor".equals(childTag)) {
214             addProcessor(DataType.PROCESS_GET, childElement);
215             ret = true;
216         } else if ("setprocessor".equals(childTag)) {
217             addProcessor(DataType.PROCESS_SET, childElement);
218             ret = true;
219         } else if ("commitprocessor".equals(childTag)) {
220             addCommitProcessor(childElement);
221             ret = true;
222         } else if ("enumeration".equals(childTag)) {
223             addEnumeration(childElement);
224             ret = true;
225         } else if ("default".equals(childTag)) {
226             String JavaDoc value = DataTypeXml.getAttribute(childElement, "value");
227             dataType.setDefaultValue(value);
228             ret = true;
229         } else if (addPatternCondition(childElement)) {
230             ret = true;
231         } else if (addPasswordProperty(childElement)) {
232             ret = true;
233         } else if (addLengthDataCondition(childElement)) {
234             ret = true;
235         } else if (addComparableCondition(childElement)) {
236             ret = true;
237         }
238         return ret;
239     }
240
241
242     private void addProcessor(int action, int processingType, Processor newProcessor) {
243         Processor oldProcessor = dataType.getProcessor(action, processingType);
244         newProcessor = DataTypeXml.chainProcessors(oldProcessor, newProcessor);
245         log.debug(dataType + " Found processor " + oldProcessor + "--> " + newProcessor);
246         dataType.setProcessor(action, newProcessor, processingType);
247     }
248
249
250     protected void addProcessor(int action, Element processorElement) {
251         Processor newProcessor = DataTypeXml.createProcessor(processorElement);
252         if (newProcessor != null) {
253             String JavaDoc type = processorElement.getAttribute("type");
254             if (type.equals("")) {
255                 addProcessor(action, Field.TYPE_UNKNOWN, newProcessor);
256             } else if (type.equals("*")) {
257                 for (int i = Fields.TYPE_MINVALUE; i <= Fields.TYPE_MAXVALUE; i++) {
258                     BasicDataType basicDataType = DataTypes.getDataType(i);
259                     int processingType = Fields.classToType(basicDataType.getTypeAsClass());
260                     addProcessor(action, processingType, newProcessor);
261                 }
262             } else {
263                 int processingType = Field.TYPE_UNKNOWN;
264                 BasicDataType basicDataType = DataTypes.getDataType(type);
265                 // this makes NO sense, processors type are assocated with bridge methods (field types) not with datatypes
266
if (basicDataType != null) {
267                     processingType = Fields.classToType(basicDataType.getTypeAsClass());
268                 } else {
269                     log.warn("Datatype " + type + " is unknown, create processor as a default processor");
270                 }
271                 addProcessor(action, processingType, newProcessor);
272             }
273         }
274     }
275
276     protected void addCommitProcessor(Element processorElement) {
277         CommitProcessor newProcessor = DataTypeXml.createCommitProcessor(processorElement);
278         CommitProcessor oldProcessor = dataType.getCommitProcessor();
279         newProcessor = DataTypeXml.chainProcessors(oldProcessor, newProcessor);
280         dataType.setCommitProcessor(newProcessor);
281     }
282
283     protected void setRestrictionData(DataType.Restriction restriction, Element element) {
284         if (DataTypeXml.hasAttribute(element, "fixed")) {
285             boolean isFixed = Boolean.valueOf(DataTypeXml.getAttribute(element, "fixed")).booleanValue();
286             restriction.setFixed(isFixed);
287         }
288         String JavaDoc enforce = DataTypeXml.getAttribute(element, "enforce").toLowerCase();
289         if (enforce.equals("absolute")) {
290             restriction.setEnforceStrength(DataType.ENFORCE_ABSOLUTE);
291         } else if (enforce.equals("always") || enforce.equals("")) {
292             restriction.setEnforceStrength(DataType.ENFORCE_ALWAYS);
293         } else if (enforce.equals("onchange")) {
294             restriction.setEnforceStrength(DataType.ENFORCE_ONCHANGE);
295         } else if (enforce.equals("oncreate")) {
296             restriction.setEnforceStrength(DataType.ENFORCE_ONCREATE);
297         } else if (enforce.equals("never")) {
298             restriction.setEnforceStrength(DataType.ENFORCE_NEVER);
299         } else {
300             log.warn("Unrecognised value for 'enforce' attribute '" + enforce + "' in " + XMLWriter.write(element, true, true));
301         }
302         LocalizedString descriptions = restriction.getErrorDescription();
303         restriction.setErrorDescription(DataTypeXml.getLocalizedDescription("description", element, descriptions, null));
304     }
305
306     /**
307      * Used the enumeration element.
308      */

309     protected void addEnumeration(Element enumerationElement) {
310         LocalizedEntryListFactory fact = dataType.getEnumerationFactory();
311         setRestrictionData(dataType.getEnumerationRestriction(), enumerationElement);
312         fact.clear();
313         fact.fillFromXml(enumerationElement, dataType.getTypeAsClass());
314     }
315
316     protected boolean setProperty(Element element) {
317         try {
318             String JavaDoc name = DataTypeXml.getAttribute(element, "name");
319             String JavaDoc methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
320             String JavaDoc value = DataTypeXml.getAttribute(element, "value");
321             Class JavaDoc claz = dataType.getClass();
322             Method JavaDoc method = claz.getMethod(methodName, new Class JavaDoc[] {String JavaDoc.class});
323             method.invoke(dataType, new Object JavaDoc[] { value });
324         } catch (NoSuchMethodException JavaDoc nsme) {
325             log.warn(nsme);
326             return false;
327         } catch (SecurityException JavaDoc se) {
328             log.warn(se);
329             return false;
330         } catch (IllegalAccessException JavaDoc iae) {
331             log.warn(iae);
332             return false;
333         } catch (InvocationTargetException JavaDoc ite) {
334             log.warn(ite);
335             return false;
336         }
337         return true;
338     }
339
340     /**
341      * Considers length related condition elements ('minLength', 'maxLength' , 'length')
342      * @return <code>true</code> if element was used
343      */

344     protected boolean addLengthDataCondition(Element conditionElement) {
345         if (dataType instanceof LengthDataType) {
346             String JavaDoc localName = conditionElement.getLocalName();
347             LengthDataType bDataType = (LengthDataType) dataType;
348             if ("minLength".equals(localName)) {
349                 long value = DataTypeXml.getLongValue(conditionElement);
350                 bDataType.setMinLength(value);
351                 setRestrictionData(bDataType.getMinLengthRestriction(), conditionElement);
352                 return true;
353             } else if ("maxLength".equals(localName)) {
354                 long value = DataTypeXml.getLongValue(conditionElement);
355                 bDataType.setMaxLength(value);
356                 setRestrictionData(bDataType.getMaxLengthRestriction(), conditionElement);
357                 return true;
358             } else if ("length".equals(localName)) {
359                 long value = DataTypeXml.getLongValue(conditionElement);
360                 bDataType.setMinLength(value);
361                 setRestrictionData(bDataType.getMinLengthRestriction(), conditionElement);
362                 bDataType.setMaxLength(value);
363                 setRestrictionData(bDataType.getMaxLengthRestriction(), conditionElement);
364                 return true;
365             }
366         }
367         return false;
368     }
369
370
371
372     protected boolean addPasswordProperty(Element propertyElement) {
373         String JavaDoc localName = propertyElement.getLocalName();
374         if ("password".equals(localName) && (dataType instanceof StringDataType)) {
375             StringDataType stringDataType = (StringDataType) dataType;
376             boolean value = Casting.toBoolean(DataTypeXml.getAttribute(propertyElement, "value"));
377             stringDataType.setPassword(value);
378             return true;
379         }
380         return false;
381     }
382
383     /**
384      * Considers the 'pattern' condition element.
385      * @return <code>true</code> if element was used
386      */

387
388     protected boolean addPatternCondition(Element conditionElement) {
389         String JavaDoc localName = conditionElement.getLocalName();
390         if (dataType instanceof StringDataType) {
391             StringDataType sDataType = (StringDataType) dataType;
392             if ("pattern".equals(localName)) {
393                 String JavaDoc value = DataTypeXml.getAttribute(conditionElement, "value");
394                 log.debug("Setting pattern on " + sDataType);
395                 sDataType.setPattern(java.util.regex.Pattern.compile(value));
396                     setRestrictionData(sDataType.getPatternRestriction(), conditionElement);
397                 return true;
398             }
399         } else if (dataType instanceof DateTimeDataType) {
400             DateTimeDataType sDataType = (DateTimeDataType) dataType;
401             if ("pattern".equals(localName)) {
402                 String JavaDoc value = DataTypeXml.getAttribute(conditionElement, "value");
403                 Locale locale = LocalizedString.getLocale(conditionElement);
404                 sDataType.setPattern(value, locale);
405                 return true;
406             }
407         } else if (dataType instanceof BinaryDataType) { // not really a condition yet.
408
BinaryDataType sDataType = (BinaryDataType) dataType;
409             if ("pattern".equals(localName)) {
410                 String JavaDoc value = DataTypeXml.getAttribute(conditionElement, "value");
411                 sDataType.setValidMimeTypes(java.util.regex.Pattern.compile(value));
412                 return true;
413             }
414        }
415         return false;
416     }
417
418     /**
419      * Considers the condition elements associated with 'Comparables' (minInclusive, maxInclusive,
420      * minExclusive, maxExclusive).
421      * @return <code>true</code> if element was used
422      */

423
424     protected boolean addComparableCondition(Element conditionElement) {
425         if (dataType instanceof ComparableDataType) {
426             String JavaDoc localName = conditionElement.getLocalName();
427             ComparableDataType dDataType = (ComparableDataType) dataType;
428             if ("minExclusive".equals(localName) || "minInclusive".equals(localName)) {
429                 Comparable JavaDoc value = (Comparable JavaDoc) dDataType.cast(DataTypeXml.getValue(conditionElement), null, null);
430                 dDataType.setMin(value, "minInclusive".equals(localName));
431                 setRestrictionData(dDataType.getMinRestriction(), conditionElement);
432                 return true;
433             } else if ("maxExclusive".equals(localName) || "maxInclusive".equals(localName)) {
434                 Comparable JavaDoc value = (Comparable JavaDoc) dDataType.cast(DataTypeXml.getValue(conditionElement), null, null);
435                 dDataType.setMax(value, "maxInclusive".equals(localName));
436                 setRestrictionData(dDataType.getMaxRestriction(), conditionElement);
437                 return true;
438             }
439         }
440         return false;
441     }
442
443
444     public String JavaDoc toString() {
445         return "definition(" + (dataType == null ? "NONE" : dataType.toString()) + ")";
446     }
447
448 }
449
Popular Tags