View Javadoc
1   /*
2    * Copyright 2004-2022 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.ibatis.common.beans;
17  
18  import com.ibatis.common.resources.Resources;
19  
20  import java.io.PrintWriter;
21  import java.io.StringWriter;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.StringTokenizer;
27  
28  import org.w3c.dom.CharacterData;
29  import org.w3c.dom.Document;
30  import org.w3c.dom.Element;
31  import org.w3c.dom.NamedNodeMap;
32  import org.w3c.dom.Node;
33  import org.w3c.dom.NodeList;
34  import org.w3c.dom.Text;
35  
36  /**
37   * A Probe implementation for working with DOM objects.
38   */
39  public class DomProbe extends BaseProbe {
40  
41    @Override
42    public String[] getReadablePropertyNames(Object object) {
43      List props = new ArrayList();
44      Element e = resolveElement(object);
45      NodeList nodes = e.getChildNodes();
46      for (int i = 0; i < nodes.getLength(); i++) {
47        props.add(nodes.item(i).getNodeName());
48      }
49      return (String[]) props.toArray(new String[props.size()]);
50    }
51  
52    @Override
53    public String[] getWriteablePropertyNames(Object object) {
54      List props = new ArrayList();
55      Element e = resolveElement(object);
56      NodeList nodes = e.getChildNodes();
57      for (int i = 0; i < nodes.getLength(); i++) {
58        props.add(nodes.item(i).getNodeName());
59      }
60      return (String[]) props.toArray(new String[props.size()]);
61    }
62  
63    public Class getPropertyTypeForSetter(Object object, String name) {
64      Element e = findNestedNodeByName(resolveElement(object), name, false);
65      // todo alias types, don't use exceptions like this
66      try {
67        return Resources.classForName(e.getAttribute("type"));
68      } catch (ClassNotFoundException e1) {
69        return Object.class;
70      }
71    }
72  
73    public Class getPropertyTypeForGetter(Object object, String name) {
74      Element e = findNestedNodeByName(resolveElement(object), name, false);
75      // todo alias types, don't use exceptions like this
76      try {
77        return Resources.classForName(e.getAttribute("type"));
78      } catch (ClassNotFoundException e1) {
79        return Object.class;
80      }
81    }
82  
83    public boolean hasWritableProperty(Object object, String propertyName) {
84      return findNestedNodeByName(resolveElement(object), propertyName, false) != null;
85    }
86  
87    public boolean hasReadableProperty(Object object, String propertyName) {
88      return findNestedNodeByName(resolveElement(object), propertyName, false) != null;
89    }
90  
91    public Object getObject(Object object, String name) {
92      Object value = null;
93      Element element = findNestedNodeByName(resolveElement(object), name, false);
94      if (element != null) {
95        value = getElementValue(element);
96      }
97      return value;
98    }
99  
100   public void setObject(Object object, String name, Object value) {
101     Element element = findNestedNodeByName(resolveElement(object), name, true);
102     if (element != null) {
103       setElementValue(element, value);
104     }
105   }
106 
107   @Override
108   protected void setProperty(Object object, String property, Object value) {
109     Element element = findNodeByName(resolveElement(object), property, 0, true);
110     if (element != null) {
111       setElementValue(element, value);
112     }
113   }
114 
115   @Override
116   protected Object getProperty(Object object, String property) {
117     Object value = null;
118     Element element = findNodeByName(resolveElement(object), property, 0, false);
119     if (element != null) {
120       value = getElementValue(element);
121     }
122     return value;
123   }
124 
125   /**
126    * Resolve element.
127    *
128    * @param object
129    *          the object
130    *
131    * @return the element
132    */
133   private Element resolveElement(Object object) {
134     Element element = null;
135     if (object instanceof Document) {
136       element = (Element) ((Document) object).getLastChild();
137     } else if (object instanceof Element) {
138       element = (Element) object;
139     } else {
140       throw new ProbeException("An unknown object type was passed to DomProbe.  Must be a Document.");
141     }
142     return element;
143   }
144 
145   /**
146    * Sets the element value.
147    *
148    * @param element
149    *          the element
150    * @param value
151    *          the value
152    */
153   private void setElementValue(Element element, Object value) {
154     CharacterData data = null;
155 
156     Element prop = element;
157 
158     if (value instanceof Collection) {
159       Iterator items = ((Collection) value).iterator();
160       while (items.hasNext()) {
161         Document valdoc = (Document) items.next();
162         NodeList list = valdoc.getChildNodes();
163         for (int i = 0; i < list.getLength(); i++) {
164           Node newNode = element.getOwnerDocument().importNode(list.item(i), true);
165           element.appendChild(newNode);
166         }
167       }
168     } else if (value instanceof Document) {
169       Document valdoc = (Document) value;
170       Node lastChild = valdoc.getLastChild();
171       NodeList list = lastChild.getChildNodes();
172       for (int i = 0; i < list.getLength(); i++) {
173         Node newNode = element.getOwnerDocument().importNode(list.item(i), true);
174         element.appendChild(newNode);
175       }
176     } else if (value instanceof Element) {
177       Node newNode = element.getOwnerDocument().importNode((Element) value, true);
178       element.appendChild(newNode);
179     } else {
180       // Find text child element
181       NodeList texts = prop.getChildNodes();
182       if (texts.getLength() == 1) {
183         Node child = texts.item(0);
184         if (child instanceof CharacterData) {
185           // Use existing text.
186           data = (CharacterData) child;
187         } else {
188           // Remove non-text, add text.
189           prop.removeChild(child);
190           Text text = prop.getOwnerDocument().createTextNode(String.valueOf(value));
191           prop.appendChild(text);
192           data = text;
193         }
194       } else if (texts.getLength() > 1) {
195         // Remove all, add text.
196         for (int i = texts.getLength() - 1; i >= 0; i--) {
197           prop.removeChild(texts.item(i));
198         }
199         Text text = prop.getOwnerDocument().createTextNode(String.valueOf(value));
200         prop.appendChild(text);
201         data = text;
202       } else {
203         // Add text.
204         Text text = prop.getOwnerDocument().createTextNode(String.valueOf(value));
205         prop.appendChild(text);
206         data = text;
207       }
208       data.setData(String.valueOf(value));
209     }
210 
211     // Set type attribute
212     // prop.setAttribute("type", value == null ? "null" : value.getClass().getName());
213 
214   }
215 
216   /**
217    * Gets the element value.
218    *
219    * @param element
220    *          the element
221    *
222    * @return the element value
223    */
224   private Object getElementValue(Element element) {
225     StringBuilder value = null;
226 
227     Element prop = element;
228 
229     if (prop != null) {
230       // Find text child elements
231       NodeList texts = prop.getChildNodes();
232       if (texts.getLength() > 0) {
233         value = new StringBuilder();
234         for (int i = 0; i < texts.getLength(); i++) {
235           Node text = texts.item(i);
236           if (text instanceof CharacterData) {
237             value.append(((CharacterData) text).getData());
238           }
239         }
240       }
241     }
242 
243     // convert to proper type
244     // value = convert(value.toString());
245 
246     if (value == null) {
247       return null;
248     } else {
249       return value.toString();
250     }
251   }
252 
253   /**
254    * Find nested node by name.
255    *
256    * @param element
257    *          the element
258    * @param name
259    *          the name
260    * @param create
261    *          the create
262    *
263    * @return the element
264    */
265   private Element findNestedNodeByName(Element element, String name, boolean create) {
266     Element child = element;
267 
268     StringTokenizer parser = new StringTokenizer(name, ".", false);
269     while (parser.hasMoreTokens()) {
270       String childName = parser.nextToken();
271       if (childName.indexOf('[') > -1) {
272         String propName = childName.substring(0, childName.indexOf('['));
273         int i = Integer.parseInt(childName.substring(childName.indexOf('[') + 1, childName.indexOf(']')));
274         child = findNodeByName(child, propName, i, create);
275       } else {
276         child = findNodeByName(child, childName, 0, create);
277       }
278       if (child == null) {
279         break;
280       }
281     }
282 
283     return child;
284   }
285 
286   /**
287    * Find node by name.
288    *
289    * @param element
290    *          the element
291    * @param name
292    *          the name
293    * @param index
294    *          the index
295    * @param create
296    *          the create
297    *
298    * @return the element
299    */
300   private Element findNodeByName(Element element, String name, int index, boolean create) {
301     Element prop = null;
302 
303     // Find named property element
304     NodeList propNodes = element.getElementsByTagName(name);
305     if (propNodes.getLength() > index) {
306       prop = (Element) propNodes.item(index);
307     } else {
308       if (create) {
309         for (int i = 0; i < index + 1; i++) {
310           prop = element.getOwnerDocument().createElement(name);
311           element.appendChild(prop);
312         }
313       }
314     }
315     return prop;
316   }
317 
318   /**
319    * Converts a DOM node to a complete xml string.
320    *
321    * @param node
322    *          - the node to process
323    * @param indent
324    *          - how to indent the children of the node
325    *
326    * @return The node as a String
327    */
328   public static String nodeToString(Node node, String indent) {
329     StringWriter stringWriter = new StringWriter();
330     PrintWriter printWriter = new PrintWriter(stringWriter);
331 
332     switch (node.getNodeType()) {
333 
334       case Node.DOCUMENT_NODE:
335         printWriter.println("<xml version=\"1.0\">\n");
336         // recurse on each child
337         NodeList nodes = node.getChildNodes();
338         if (nodes != null) {
339           for (int i = 0; i < nodes.getLength(); i++) {
340             printWriter.print(nodeToString(nodes.item(i), ""));
341           }
342         }
343         break;
344 
345       case Node.ELEMENT_NODE:
346         String name = node.getNodeName();
347         printWriter.print(indent + "<" + name);
348         NamedNodeMap attributes = node.getAttributes();
349         for (int i = 0; i < attributes.getLength(); i++) {
350           Node current = attributes.item(i);
351           printWriter.print(" " + current.getNodeName() + "=\"" + current.getNodeValue() + "\"");
352         }
353         printWriter.print(">");
354 
355         // recurse on each child
356         NodeList children = node.getChildNodes();
357         if (children != null) {
358           for (int i = 0; i < children.getLength(); i++) {
359             printWriter.print(nodeToString(children.item(i), indent + indent));
360           }
361         }
362 
363         printWriter.print("</" + name + ">");
364         break;
365 
366       case Node.TEXT_NODE:
367         printWriter.print(node.getNodeValue());
368         break;
369     }
370 
371     printWriter.flush();
372     String result = stringWriter.getBuffer().toString();
373     printWriter.close();
374 
375     return result;
376   }
377 
378 }