KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > bridge > util > xml > Generator


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
11 package org.mmbase.bridge.util.xml;
12
13 import org.w3c.dom.*;
14 import org.w3c.dom.Node JavaDoc;
15 import org.w3c.dom.NodeList JavaDoc;
16 import javax.xml.parsers.DocumentBuilder JavaDoc;
17 import org.mmbase.bridge.*;
18
19 import org.mmbase.util.logging.*;
20 import org.mmbase.util.xml.XMLWriter;
21
22
23 /**
24  * Uses the XML functions from the bridge to construct a DOM document representing MMBase data structures.
25  *
26  * @author Michiel Meeuwissen
27  * @author Eduard Witteveen
28  * @version $Id: Generator.java,v 1.39 2006/02/17 21:16:57 michiel Exp $
29  * @since MMBase-1.6
30  */

31 public class Generator {
32
33     private static final Logger log = Logging.getLoggerInstance(Generator.class);
34
35     public final static String JavaDoc NAMESPACE = "http://www.mmbase.org/xmlns/objects";
36     private final static String JavaDoc DOCUMENTTYPE_PUBLIC = "-//MMBase//DTD objects config 1.0//EN";
37     private final static String JavaDoc DOCUMENTTYPE_SYSTEM = "http://www.mmbase.org/dtd/objects_1_0.dtd";
38
39     private Document document = null;
40     private DocumentBuilder JavaDoc documentBuilder = null;
41     private Cloud cloud = null;
42
43     private boolean namespaceAware = false;
44
45     /**
46      * To create documents representing structures from the cloud, it
47      * needs a documentBuilder, to contruct the DOM Document, and the
48      * cloud from which the data to be inserted will come from.
49      *
50      * @param documentBuilder The DocumentBuilder which will be used to create the Document.
51      * @param cloud The cloud from which the data will be.
52      * @see org.mmbase.util.xml.DocumentReader#getDocumentBuilder()
53      */

54     public Generator(DocumentBuilder JavaDoc documentBuilder, Cloud cloud) {
55         this.documentBuilder = documentBuilder;
56         this.cloud = cloud;
57
58     }
59
60     public Generator(DocumentBuilder JavaDoc documentBuilder) {
61         this(documentBuilder, null);
62     }
63
64     public Generator(Document doc) {
65         document = doc;
66         namespaceAware = document.getDocumentElement().getNamespaceURI() != null;
67     }
68
69     /**
70      * Returns the working DOM document.
71      * @return The document, build with the operations done on the generator class
72      */

73     public Document getDocument() {
74         if (document == null) {
75             DOMImplementation impl = documentBuilder.getDOMImplementation();
76             document = impl.createDocument(namespaceAware ? NAMESPACE : null,
77                                            "objects",
78                                            impl.createDocumentType("objects", DOCUMENTTYPE_PUBLIC, DOCUMENTTYPE_SYSTEM)
79                                            );
80             if (cloud != null) {
81                 addCloud();
82             }
83         }
84         return document;
85     }
86
87     /**
88      * If namespace aware, element are created with the namespace http://www.mmbase.org/objects,
89      * otherwise, without namespace.
90      * @since MMBase-1.8
91      */

92     public void setNamespaceAware(boolean n) {
93         if (document != null) throw new IllegalStateException JavaDoc("Already started constructing");
94         namespaceAware = n;
95     }
96
97     /**
98      * @since MMBase-1.8
99      */

100     public boolean isNamespaceAware() {
101         return namespaceAware;
102     }
103
104     /**
105      * @since MMBase-1.8
106      */

107     protected Element createElement(String JavaDoc name) {
108         getDocument();
109         if (namespaceAware) {
110             return document.createElementNS(NAMESPACE, name);
111         } else {
112             return document.createElement(name);
113         }
114                 
115     }
116     protected final void setAttribute(Element element, String JavaDoc name, String JavaDoc value) {
117         // attributes normally have no namespace. You can assign one, but then they will always have
118
// to be indicated explicitely (in controdiction to elements).
119
// So attributes are created without namespace.
120
/*
121         if (namespaceAware) {
122             element.setAttributeNS(NAMESPACE, name, value);
123         } else {
124             element.setAttribute(name, value);
125         }
126         */

127         element.setAttribute(name, value);
128     }
129     
130     protected final String JavaDoc getAttribute(Element element, String JavaDoc name) {
131         // see setAttribute
132
/*
133         if (namespaceAware) {
134             return element.getAttributeNS(NAMESPACE, name);
135         } else {
136             return element.getAttribute(name);
137         }
138         */

139         return element.getAttribute(name);
140     }
141
142     /**
143      * Returns the document as a String.
144      * @return the xml generated as an string
145      */

146     public String JavaDoc toString() {
147         return toString(false);
148     }
149
150     /**
151      * Returns the document as a String.
152      * @param ident if the string has to be idented
153      * @return the generated xml as a (formatted) string
154      */

155     public String JavaDoc toString(boolean ident) {
156         return XMLWriter.write(document, ident);
157     }
158
159     private void addCloud() {
160         setAttribute(document.getDocumentElement(), "cloud", cloud.getName());
161     }
162
163     /**
164      * Adds a field to the DOM Document. This means that there will
165      * also be added a Node if this is necessary.
166      * @param node An MMbase bridge Node.
167      * @param fieldDefinition An MMBase bridge Field.
168      */

169     public Element add(org.mmbase.bridge.Node node, Field fieldDefinition) {
170         getDocument();
171         if (cloud == null) {
172             cloud = node.getCloud();
173             addCloud();
174         }
175
176         Element object = getNode(node);
177
178         if (! (object.getFirstChild() instanceof Element)) {
179             log.warn("Cannot find first field of " + XMLWriter.write(object, false));
180             return object;
181         }
182         // get the field...
183
Element field = (Element)object.getFirstChild();
184         while (field != null && !fieldDefinition.getName().equals(getAttribute(field, "name"))) {
185             field = (Element)field.getNextSibling();
186         }
187         // when not found, we are in a strange situation..
188
if(field == null) throw new BridgeException("field with name: " + fieldDefinition.getName() + " of node " + node.getNumber() + " with nodetype: " + fieldDefinition.getNodeManager().getName() + " not found, while it should be in the node skeleton.. xml:\n" + toString(true));
189         // when it is filled (allready), we can return
190
if (field.getTagName().equals("field")) {
191             return field;
192         }
193
194         // was not filled, so fill it... first remove the unfilled
195
Element filledField = createElement("field");
196
197         field.getParentNode().replaceChild(filledField, field);
198         field = filledField;
199         // the name
200
setAttribute(field, "name", fieldDefinition.getName());
201         // now fill it with the new info...
202
// format
203
setAttribute(field, "format", getFieldFormat(fieldDefinition));
204         // the value
205
switch (fieldDefinition.getType()) {
206         case Field.TYPE_XML :
207             Document doc = node.getXMLValue(fieldDefinition.getName());
208             // only fill the field, if field has a value..
209
if (doc != null) {
210                 // put the xml inside the field...
211
field.appendChild(importDocument(field, doc));
212             }
213             break;
214         case Field.TYPE_BINARY :
215             org.mmbase.util.transformers.Base64 transformer = new org.mmbase.util.transformers.Base64();
216             field.appendChild(document.createTextNode(transformer.transform(node.getByteValue(fieldDefinition.getName()))));
217             break;
218         case Field.TYPE_DATETIME :
219             // shoudlw e use ISO_8601_LOOSE here or ISO_8601_UTC?
220
field.appendChild(document.createTextNode(org.mmbase.util.Casting.ISO_8601_LOOSE.format(node.getDateValue(fieldDefinition.getName()))));
221             break;
222         default :
223             field.appendChild(document.createTextNode(node.getStringValue(fieldDefinition.getName())));
224         }
225         // or do we need more?
226
return field;
227     }
228
229     /**
230      * Adds one Node to a DOM Document.
231      * @param node An MMBase bridge Node.
232      */

233     public Element add(org.mmbase.bridge.Node node) {
234         // process all the fields..
235
NodeManager nm = node.getNodeManager();
236         FieldIterator i = nm.getFields(NodeManager.ORDER_CREATE).fieldIterator();
237         while (i.hasNext()) {
238             Field field = i.nextField();
239             if (field.getType() != Field.TYPE_BINARY) {
240                 add(node, field);
241             }
242         }
243         return getNode(node);
244     }
245
246     /**
247      * Adds one Relation to a DOM Document.
248      * @param relation An MMBase bridge Node.
249      */

250     public Element add(Relation relation) {
251         return add((org.mmbase.bridge.Node)relation);
252
253     }
254
255     /**
256      * Adds a whole MMBase bridge NodeList to the DOM Document.
257      * @param nodes An MMBase bridge NodeList.
258      */

259     public void add(org.mmbase.bridge.NodeList nodes) {
260         NodeIterator i = nodes.nodeIterator();
261         while (i.hasNext()) {
262             add(i.nextNode());
263         }
264     }
265
266     /**
267      * Adds a list of Relation to the DOM Document.
268      * @param relations An MMBase bridge RelationList
269      */

270     public void add(RelationList relations) {
271         RelationIterator i = relations.relationIterator();
272         while (i.hasNext()) {
273             add(i.nextRelation());
274
275         }
276     }
277
278     protected Element getElementById(Node JavaDoc n, String JavaDoc id) {
279
280         NodeList JavaDoc list = n.getChildNodes();
281         for (int i = 0 ; i < list.getLength(); i++) {
282             Node JavaDoc node = list.item(i);
283             if (node instanceof Element) {
284                 if (getAttribute((Element) node, "id").equals(id)) {
285                     return (Element) node;
286                 }
287             }
288         }
289         for (int i = 0 ; i < list.getLength(); i++) {
290             Node JavaDoc node = list.item(i);
291             Element subs = getElementById(node, id);
292             if (subs != null) return subs;
293         }
294         return null;
295         
296     }
297     /**
298      * Creates an Element which represents a bridge.Node with all fields unfilled.
299      * @param node MMbase node
300      * @return Element which represents a bridge.Node
301      */

302     private Element getNode(org.mmbase.bridge.Node node) {
303
304         // if we are a relation,.. behave like one!
305
// why do we find it out now, and not before?
306
boolean getElementByIdWorks = false;
307         Element object = null;
308         if (getElementByIdWorks) {
309             // Michiel: I tried it by specifieing id as ID in dtd, but that also doesn't make it work.
310
object = getDocument().getElementById("" + node.getNumber());
311         } else {
312             object = getElementById(getDocument(), "" + node.getNumber());
313         }
314
315         if (object != null)
316             return object;
317
318         // if it is a realtion... first add source and destination attributes..
319
// can only happen after the node = node.getCloud().getNode(node.getNumber()); thing!
320
if (node instanceof Relation) {
321             Relation relation = (Relation)node;
322             getNode(relation.getSource()).appendChild(createRelationEntry(relation, relation.getSource()));
323             getNode(relation.getDestination()).appendChild(createRelationEntry(relation, relation.getDestination()));
324         }
325
326         // node didnt exist, so we need to create it...
327
object = createElement("object");
328
329         setAttribute(object, "id", "" + node.getNumber());
330         setAttribute(object, "type", node.getNodeManager().getName());
331         // and the otype (type as number)
332
setAttribute(object, "otype", node.getStringValue("otype"));
333
334         // add the fields (empty)
335
// While still having 'unfilledField's
336
// you know that the node is not yet presented completely.
337

338         FieldIterator i = node.getNodeManager().getFields(NodeManager.ORDER_CREATE).fieldIterator();
339         while (i.hasNext()) {
340             Field fieldDefinition = i.nextField();
341             Element field = createElement("unfilledField");
342
343             // the name
344
setAttribute(field, "name", fieldDefinition.getName());
345             // add it to the object
346
object.appendChild(field);
347         }
348         document.getDocumentElement().appendChild(object);
349         return object;
350     }
351
352     /**
353      * Imports an XML document as a value of a field. Can be any XML, so the namespace is set.
354      *
355      * @param fieldElement The Element describing the field
356      * @param toImport The Document to set as the field's value
357      * @return The fieldContent.
358      */

359     private Element importDocument(Element fieldElement, Document toImport) {
360         DocumentType dt = toImport.getDoctype();
361         String JavaDoc tagName = toImport.getDocumentElement().getTagName();
362
363         String JavaDoc namespace;
364         if (dt != null) {
365             namespace = dt.getSystemId();
366         } else {
367             namespace = "http://www.mmbase.org/xmlns/" + tagName;
368         }
369         if (log.isDebugEnabled()) {
370             log.debug("using namepace: " + namespace);
371         }
372         Element fieldContent = (Element)document.importNode(toImport.getDocumentElement(), true);
373         fieldContent.setAttribute("xmlns", namespace);
374         fieldElement.appendChild(fieldContent);
375         return fieldContent;
376     }
377
378     private String JavaDoc getFieldFormat(Field field) {
379         switch (field.getType()) {
380         case Field.TYPE_XML :
381             return "xml";
382         case Field.TYPE_STRING :
383             return "string";
384         case Field.TYPE_NODE :
385             return "object"; // better would be "node" ?
386
case Field.TYPE_INTEGER :
387         case Field.TYPE_LONG :
388             // was it a builder?
389
String JavaDoc fieldName = field.getName();
390             String JavaDoc guiType = field.getGUIType();
391
392             // I want a object database type!
393
if (fieldName.equals("otype")
394                 || fieldName.equals("number")
395                 || fieldName.equals("snumber")
396                 || fieldName.equals("dnumber")
397                 || fieldName.equals("rnumber")
398                 || fieldName.equals("role")
399                 || guiType.equals("reldefs")) {
400                     return "object"; // better would be "node" ?
401
}
402             if (guiType.equals("eventtime")) {
403                 return "date";
404             }
405         case Field.TYPE_FLOAT :
406         case Field.TYPE_DOUBLE :
407             return "numeric";
408         case Field.TYPE_BINARY :
409             return "bytes";
410         case Field.TYPE_DATETIME:
411             return "datetime";
412         case Field.TYPE_BOOLEAN:
413             return "boolean";
414         case Field.TYPE_LIST:
415             return "list";
416         default :
417             throw new RuntimeException JavaDoc("could not find field-type for:" + field.getType() + " for field: " + field);
418         }
419     }
420
421     private Element createRelationEntry(Relation relation, org.mmbase.bridge.Node relatedNode) {
422         Element fieldElement = createElement("relation");
423         // we have to know what the relation type was...
424
org.mmbase.bridge.Node reldef = cloud.getNode(relation.getStringValue("rnumber"));
425
426         setAttribute(fieldElement, "object", "" + relation.getNumber());
427
428         if (relation.getSource().getNumber() == relatedNode.getNumber()) {
429             setAttribute(fieldElement, "role", reldef.getStringValue("sname"));
430             setAttribute(fieldElement, "related", "" + relation.getDestination().getNumber());
431             setAttribute(fieldElement, "type", "source");
432         } else {
433             setAttribute(fieldElement, "role", reldef.getStringValue("dname"));
434             setAttribute(fieldElement, "related", "" + relation.getSource().getNumber());
435             setAttribute(fieldElement, "type", "destination");
436         }
437         return fieldElement;
438     }
439 }
440
Popular Tags