KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > simplexml > SimpleXMLElement


1 /*
2  * Copyright (c) 1998-2005 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Charles Reich
27  */

28
29
30 package com.caucho.quercus.lib.simplexml;
31
32 import com.caucho.quercus.env.*;
33 import com.caucho.vfs.WriteStream;
34
35 import org.w3c.dom.Document JavaDoc;
36 import org.w3c.dom.Element JavaDoc;
37 import org.w3c.dom.NamedNodeMap JavaDoc;
38 import org.w3c.dom.Node JavaDoc;
39 import org.w3c.dom.NodeList JavaDoc;
40 import org.xml.sax.InputSource JavaDoc;
41
42 import javax.xml.xpath.XPath JavaDoc;
43 import javax.xml.xpath.XPathConstants JavaDoc;
44 import javax.xml.xpath.XPathExpressionException JavaDoc;
45 import javax.xml.xpath.XPathFactory JavaDoc;
46 import java.io.ByteArrayInputStream JavaDoc;
47 import java.io.IOException JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.IdentityHashMap JavaDoc;
50 import java.util.Iterator JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.Set JavaDoc;
53
54 /**
55  * SimpleXMLElement object oriented API facade
56  */

57
58 public class SimpleXMLElement
59 {
60   private Env _env;
61   private Document JavaDoc _document;
62   private Element JavaDoc _element;
63
64   private Value _attributes;
65
66   private HashMap JavaDoc<StringValue, Value> _childMap;
67
68
69   /**
70    * need to pass document for setting text values
71    *
72    * @param document
73    * @param element
74    */

75   public SimpleXMLElement(Env env,
76               Document JavaDoc document,
77                           Element JavaDoc element)
78   {
79     _env = env;
80     _document = document;
81     _element = element;
82   }
83
84   /**
85    *
86    * @return either ArrayValueImpl or NullValue.NULL
87    */

88   public Value attributes()
89   {
90     NamedNodeMap JavaDoc attrs = _element.getAttributes();
91     int attrLength = attrs.getLength();
92
93     if (attrLength > 0) {
94       _attributes = new ArrayValueImpl();
95
96       for (int j=0; j < attrLength; j++) {
97         Node JavaDoc attribute = attrs.item(j);
98         StringValue nodeName = new StringValueImpl(attribute.getNodeName());
99         StringValue nodeValue = new StringValueImpl(attribute.getNodeValue());
100         _attributes.put(nodeName, nodeValue);
101       }
102
103     } else {
104
105       _attributes = NullValue.NULL;
106
107     }
108
109     return _attributes;
110   }
111
112   /**
113    *
114    */

115   public void fillChildMap()
116   {
117     NodeList JavaDoc children = _element.getChildNodes();
118     int nodeLength = children.getLength();
119
120     _childMap = new HashMap JavaDoc<StringValue, Value>();
121
122     if (nodeLength == 0)
123       return;
124
125     // If <foo><bar>misc</bar></foo>, then put (bar , misc)
126
// into foo's _childMap
127
if ((nodeLength == 1) && (children.item(0).getNodeType() == Node.TEXT_NODE)) {
128       String JavaDoc value = children.item(0).getNodeValue();
129
130       _childMap.put(new StringValueImpl("0"), new StringValueImpl(value));
131     } else {
132       for (int i=0; i < nodeLength; i++) {
133         Node JavaDoc child = children.item(i);
134
135         if (child.getNodeType() != Node.ELEMENT_NODE)
136           continue;
137
138         StringValue childTagName = new StringValueImpl(child.getNodeName());
139
140         // Check to see if this is the first instance of a child
141
// with this NodeName. If so create a new ArrayValueImpl,
142
// if not add to existing ArrayValueImpl
143
ArrayValue childArray = (ArrayValue) _childMap.get(childTagName);
144         if (childArray == null) {
145           childArray = new SimpleXMLElementArray();
146           _childMap.put(childTagName, childArray);
147         }
148
149         childArray.put(_env.wrapJava(new SimpleXMLElement(_env, _document, (Element JavaDoc) child)));
150       }
151
152       // Iterate over _childMap and replace all entries with only one
153
// element from ArrayValue to SimpleXMLElement
154
Set JavaDoc keyValues = _childMap.entrySet();
155       int keyLength = keyValues.size();
156       Iterator JavaDoc keyIterator = keyValues.iterator();
157       for (int i=0; i < keyLength; i++) {
158         Map.Entry JavaDoc entry = (Map.Entry JavaDoc) keyIterator.next();
159         ArrayValue childArray = (ArrayValue) entry.getValue();
160     
161         if (childArray.getSize() == 1) {
162       Value value = childArray.get(LongValue.ZERO);
163
164           _childMap.put((StringValue) entry.getKey(), value);
165     }
166       }
167     }
168   }
169
170   /**
171    * NOTE: this function does not use fillChildren()
172    *
173    * @return shallow array of immediate children()
174    */

175   public Value children()
176   {
177     ArrayValue childArray = new ArrayValueImpl();
178
179     NodeList JavaDoc children = _element.getChildNodes();
180     int nodeLength = children.getLength();
181
182     for (int i = 0; i < nodeLength; i++) {
183       Node JavaDoc child = children.item(i);
184
185       if (child.getNodeType() != Node.ELEMENT_NODE)
186         continue;
187
188       childArray.put(_env.wrapJava(new SimpleXMLElement(_env, _document, (Element JavaDoc) child)));
189     }
190
191     if (childArray.getSize() > 0)
192       return childArray;
193     else
194       return NullValue.NULL;
195   }
196
197   /**
198    *
199    * @param name
200    * @return ArrayValue or null
201    */

202   public Value __getField(String JavaDoc name)
203   {
204     SimpleXMLElementArray simpleArray = new SimpleXMLElementArray();
205
206     NodeList JavaDoc children = _element.getChildNodes();
207     int length = children.getLength();
208
209     for (int i=0; i < length; i++) {
210       Node JavaDoc child = children.item(i);
211
212       if (child.getNodeType() != Node.ELEMENT_NODE)
213         continue;
214
215       //Need to always return SimpleXMLElementArray (even if only 1 element)
216
//because $foo->name[0] is valid
217
if (name.equals(child.getNodeName())) {
218         simpleArray.put(_env.wrapJava(new SimpleXMLElement(_env, _document, (Element JavaDoc) child)));
219       }
220     }
221
222     return simpleArray;
223   }
224
225   /**
226    * gets the attribute 'name' from SimpleXMLElement
227    * @param name
228    * @return StringValue or null
229    */

230   public Value __get(Value name)
231   {
232     NamedNodeMap JavaDoc attrs = _element.getAttributes();
233     int attrLength = attrs.getLength();
234     String JavaDoc attrName = name.toString();
235
236     for (int i = 0; i < attrLength; i++) {
237       Node JavaDoc attr = attrs.item(i);
238       if (attrName.equals(attr.getNodeName()))
239         return new StringValueImpl(attr.getNodeValue());
240     }
241
242     return NullValue.NULL;
243   }
244
245   /**
246    * $foo['name'] = value
247    *
248    * @param name
249    * @param value
250    */

251   public void __set(String JavaDoc name, String JavaDoc value)
252   {
253     _element.setAttribute(name, value);
254
255   }
256
257   /**
258    * Converts to a string.
259    */

260   public String JavaDoc toString()
261   {
262     //If this is a text node, then print the value
263
NodeList JavaDoc children = _element.getChildNodes();
264     if ((children.getLength() == 1)
265     && (children.item(0).getNodeType() == Node.TEXT_NODE)) {
266       return children.item(0).getNodeValue();
267     }
268
269     return "";
270   }
271 /*
272   public Element getElement()
273   {
274     return _element;
275   }
276 */

277   /**
278    * XXX: Currently does not work for $xml->inside->wayinside = "foo";
279    *
280    * $xml = simpleXML_load_string("<foo><inside><wayinside>bar</wayinside></inside></foo>");
281    *
282    * need to check for the following 2 cases:
283    * $xml->inside->wayinside = "New Value";
284    * or
285    * $temp = $xml->inside;
286    * $temp->wayinside = "New Value;
287    *
288    * Both are valid
289    *
290    */

291
292   public Value __setField(Env env, String JavaDoc name, String JavaDoc value)
293   {
294     // find Node name
295
// $xml->foo = "bar"; (NOT $xml["foo"] = "bar";)
296
NodeList JavaDoc children = _element.getChildNodes();
297     int length = children.getLength();
298
299     if (length == 0)
300       return NullValue.NULL;
301
302     // loop through all children. If there is more than one
303
// env.warning
304
int indexOfFoundChild = -1;
305     for(int i = 0; i < length; i++) {
306       if (name.equals(children.item(i).getNodeName())) {
307         if (indexOfFoundChild != -1) {
308           env.warning("Cannot assign to an array of nodes (duplicate subnodes or attr detected)");
309           return NullValue.NULL;
310         }
311         indexOfFoundChild = i;
312       }
313     }
314
315     if (indexOfFoundChild != -1) {
316       Node JavaDoc child = children.item(indexOfFoundChild);
317
318       while (child.hasChildNodes())
319         child.removeChild(child.getFirstChild());
320
321       child.appendChild(_document.createTextNode(value));
322     }
323
324     return NullValue.NULL;
325   }
326
327   /**
328    *
329    * @return this SimpleXMLElement as well formed XML
330    */

331   public Value asXML()
332   {
333     return new StringValueImpl(DOMNodeUtil.asXML(_element).toString());
334   }
335
336   /**
337    * Does not support relative xpath
338    *
339    * @param path
340    * @return NodeList
341    * @throws XPathExpressionException
342    */

343   public Value xpath(String JavaDoc path)
344     throws XPathExpressionException JavaDoc
345   {
346     XPath JavaDoc xpath = XPathFactory.newInstance().newXPath();
347
348     InputSource JavaDoc is = new InputSource JavaDoc(new ByteArrayInputStream JavaDoc(asXML().toString().getBytes()));
349
350     NodeList JavaDoc nodes = (NodeList JavaDoc) xpath.evaluate(path, is, XPathConstants.NODESET);
351
352     int nodeLength = nodes.getLength();
353
354     if (nodeLength == 0)
355       return NullValue.NULL;
356
357     // There are matching nodes
358
Value result = new SimpleXMLElementArray();
359     for (int i = 0; i < nodeLength; i++) {
360       result.put(_env.wrapJava(new SimpleXMLElement(_env, _document, (Element JavaDoc) nodes.item(i))));
361     }
362
363     return result;
364   }
365
366   private void printAttributes(WriteStream out,
367                                int depth)
368     throws IOException JavaDoc
369   {
370     attributes();
371
372     // Print attributes if not null
373
if (_attributes != NullValue.NULL) {
374       printDepth(out, 4 * (depth + 1));
375       out.println("[@attributes] => Array");
376       printDepth(out, 4 * (depth + 2));
377       out.println('(');
378
379       // Iterate through each attribute ([name] => value)
380
for (Map.Entry JavaDoc<Value, Value> mapEntry : ((ArrayValue) _attributes).entrySet()) {
381         ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry;
382         printDepth(out, 4 * (depth + 3));
383         out.println("[" + entry.getKey().toString() + "] => " + entry.getValue().toString());
384       }
385       out.println();
386       printDepth(out, 4 * (depth + 2));
387       out.println(')');
388     }
389   }
390
391   public void printRImpl(Env env,
392                          WriteStream out,
393                          int depth,
394                          IdentityHashMap JavaDoc<Value, String JavaDoc> valueSet)
395     throws IOException JavaDoc, Throwable JavaDoc
396   {
397     if (depth == 0) {
398       out.println("SimpleXMLElement Object");
399       out.println('(');
400     }
401
402     // Need to refill _childMap because elements might have been
403
// replaced
404
fillChildMap();
405
406     Set JavaDoc keyValues = _childMap.entrySet();
407     int keyLength = keyValues.size();
408     Iterator JavaDoc keyIterator = keyValues.iterator();
409     Map.Entry JavaDoc entry;
410
411     if (keyIterator.hasNext()) {
412       entry = (Map.Entry JavaDoc) keyIterator.next();
413
414       if (entry.getValue() instanceof StringValue) {
415         if (depth == 0) {
416           printDepth(out, 4);
417           out.print("[0] => ");
418         }
419
420         out.println(entry.getValue().toString());
421       } else {
422         if (depth != 0) {
423           out.println("SimpleXMLElement Object");
424
425           printDepth(out, 4 * depth);
426           out.println('(');
427         }
428
429         printAttributes(out, depth);
430
431         // loop through each element of _childMap
432
// but first reset iterator
433
keyIterator = keyValues.iterator();
434         for (int i = 0; i < keyLength; i++) {
435           entry = (Map.Entry JavaDoc) keyIterator.next();
436           printDepth(out, 4 * (depth + 1));
437           out.print("[" + entry.getKey() + "] => ");
438           if (entry.getValue() instanceof ArrayValue) {
439             out.println("Array");
440             printDepth(out, 4 * (depth + 2));
441             out.println('(');
442             // Iterate through each SimpleXMLElement
443
for (Map.Entry JavaDoc<Value, Value> mapEntry : ((ArrayValue) entry.getValue()).entrySet()) {
444               printDepth(out, 4 * (depth + 3));
445               out.print("[" + mapEntry.getKey().toString() + "] => ");
446               mapEntry.getValue().printR(env, out, depth + 4, valueSet);
447               out.println();
448             }
449             printDepth(out, 4 * (depth + 2));
450             out.println(')');
451           } else
452             ((Value) entry.getValue()).printR(env, out, depth + 2, valueSet);
453
454           out.println();
455         }
456
457         //Print closing parenthesis
458
if (depth != 0) {
459           printDepth(out, 4 * depth);
460           out.println(")");
461         }
462       }
463     } else {
464       if (depth != 0) {
465         out.println("SimpleXMLElement Object");
466
467         printDepth(out, 4 * depth);
468         out.println('(');
469
470         printAttributes(out, depth);
471         printDepth(out, 4 * depth);
472         out.println(')');
473       }
474     }
475
476     if (depth == 0)
477       out.println(')');
478   }
479
480   protected void printDepth(WriteStream out, int depth)
481     throws IOException JavaDoc
482   {
483     for (int i = 0; i < depth; i++)
484       out.print(' ');
485   }
486 }
487
Popular Tags