001: /*
002: * Copyright 2004 Clinton Begin
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package com.ibatis.common.beans;
017:
018: import java.io.PrintWriter;
019: import java.io.StringWriter;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.StringTokenizer;
025:
026: import org.w3c.dom.CharacterData;
027: import org.w3c.dom.Document;
028: import org.w3c.dom.Element;
029: import org.w3c.dom.NamedNodeMap;
030: import org.w3c.dom.Node;
031: import org.w3c.dom.NodeList;
032: import org.w3c.dom.Text;
033:
034: import com.ibatis.common.resources.Resources;
035:
036: /**
037: * A Probe implementation for working with DOM objects
038: */
039: public class DomProbe extends BaseProbe {
040:
041: public String[] getReadablePropertyNames(Object object) {
042: List props = new ArrayList();
043: Element e = resolveElement(object);
044: NodeList nodes = e.getChildNodes();
045: for (int i = 0; i < nodes.getLength(); i++) {
046: props.add(nodes.item(i).getNodeName());
047: }
048: return (String[]) props.toArray(new String[props.size()]);
049: }
050:
051: public String[] getWriteablePropertyNames(Object object) {
052: List props = new ArrayList();
053: Element e = resolveElement(object);
054: NodeList nodes = e.getChildNodes();
055: for (int i = 0; i < nodes.getLength(); i++) {
056: props.add(nodes.item(i).getNodeName());
057: }
058: return (String[]) props.toArray(new String[props.size()]);
059: }
060:
061: public Class getPropertyTypeForSetter(Object object, String name) {
062: Element e = findNestedNodeByName(resolveElement(object), name,
063: false);
064: //todo alias types, don't use exceptions like this
065: try {
066: return Resources.classForName(e.getAttribute("type"));
067: } catch (ClassNotFoundException e1) {
068: return Object.class;
069: }
070: }
071:
072: public Class getPropertyTypeForGetter(Object object, String name) {
073: Element e = findNestedNodeByName(resolveElement(object), name,
074: false);
075: //todo alias types, don't use exceptions like this
076: try {
077: return Resources.classForName(e.getAttribute("type"));
078: } catch (ClassNotFoundException e1) {
079: return Object.class;
080: }
081: }
082:
083: public boolean hasWritableProperty(Object object,
084: String propertyName) {
085: return findNestedNodeByName(resolveElement(object),
086: propertyName, false) != null;
087: }
088:
089: public boolean hasReadableProperty(Object object,
090: String propertyName) {
091: return findNestedNodeByName(resolveElement(object),
092: propertyName, false) != null;
093: }
094:
095: public Object getObject(Object object, String name) {
096: Object value = null;
097: Element element = findNestedNodeByName(resolveElement(object),
098: name, false);
099: if (element != null) {
100: value = getElementValue(element);
101: }
102: return value;
103: }
104:
105: public void setObject(Object object, String name, Object value) {
106: Element element = findNestedNodeByName(resolveElement(object),
107: name, true);
108: if (element != null) {
109: setElementValue(element, value);
110: }
111: }
112:
113: protected void setProperty(Object object, String property,
114: Object value) {
115: Element element = findNodeByName(resolveElement(object),
116: property, 0, true);
117: if (element != null) {
118: setElementValue(element, value);
119: }
120: }
121:
122: protected Object getProperty(Object object, String property) {
123: Object value = null;
124: Element element = findNodeByName(resolveElement(object),
125: property, 0, false);
126: if (element != null) {
127: value = getElementValue(element);
128: }
129: return value;
130: }
131:
132: private Element resolveElement(Object object) {
133: Element element = null;
134: if (object instanceof Document) {
135: element = (Element) ((Document) object).getLastChild();
136: } else if (object instanceof Element) {
137: element = (Element) object;
138: } else {
139: throw new ProbeException(
140: "An unknown object type was passed to DomProbe. Must be a Document.");
141: }
142: return element;
143: }
144:
145: private void setElementValue(Element element, Object value) {
146: CharacterData data = null;
147:
148: Element prop = element;
149:
150: if (value instanceof Collection) {
151: Iterator items = ((Collection) value).iterator();
152: while (items.hasNext()) {
153: Document valdoc = (Document) items.next();
154: NodeList list = valdoc.getChildNodes();
155: for (int i = 0; i < list.getLength(); i++) {
156: Node newNode = element.getOwnerDocument()
157: .importNode(list.item(i), true);
158: element.appendChild(newNode);
159: }
160: }
161: } else if (value instanceof Document) {
162: Document valdoc = (Document) value;
163: Node lastChild = valdoc.getLastChild();
164: NodeList list = lastChild.getChildNodes();
165: for (int i = 0; i < list.getLength(); i++) {
166: Node newNode = element.getOwnerDocument().importNode(
167: list.item(i), true);
168: element.appendChild(newNode);
169: }
170: } else if (value instanceof Element) {
171: Node newNode = element.getOwnerDocument().importNode(
172: (Element) value, true);
173: element.appendChild(newNode);
174: } else {
175: // Find text child element
176: NodeList texts = prop.getChildNodes();
177: if (texts.getLength() == 1) {
178: Node child = texts.item(0);
179: if (child instanceof CharacterData) {
180: // Use existing text.
181: data = (CharacterData) child;
182: } else {
183: // Remove non-text, add text.
184: prop.removeChild(child);
185: Text text = prop.getOwnerDocument().createTextNode(
186: String.valueOf(value));
187: prop.appendChild(text);
188: data = text;
189: }
190: } else if (texts.getLength() > 1) {
191: // Remove all, add text.
192: for (int i = texts.getLength() - 1; i >= 0; i--) {
193: prop.removeChild(texts.item(i));
194: }
195: Text text = prop.getOwnerDocument().createTextNode(
196: String.valueOf(value));
197: prop.appendChild(text);
198: data = text;
199: } else {
200: // Add text.
201: Text text = prop.getOwnerDocument().createTextNode(
202: String.valueOf(value));
203: prop.appendChild(text);
204: data = text;
205: }
206: data.setData(String.valueOf(value));
207: }
208:
209: // Set type attribute
210: //prop.setAttribute("type", value == null ? "null" : value.getClass().getName());
211:
212: }
213:
214: private Object getElementValue(Element element) {
215: StringBuffer value = null;
216:
217: Element prop = element;
218:
219: if (prop != null) {
220: // Find text child elements
221: NodeList texts = prop.getChildNodes();
222: if (texts.getLength() > 0) {
223: value = new StringBuffer();
224: for (int i = 0; i < texts.getLength(); i++) {
225: Node text = texts.item(i);
226: if (text instanceof CharacterData) {
227: value.append(((CharacterData) text).getData());
228: }
229: }
230: }
231: }
232:
233: //convert to proper type
234: //value = convert(value.toString());
235:
236: if (value == null) {
237: return null;
238: } else {
239: return String.valueOf(value);
240: }
241: }
242:
243: private Element findNestedNodeByName(Element element, String name,
244: boolean create) {
245: Element child = element;
246:
247: StringTokenizer parser = new StringTokenizer(name, ".", false);
248: while (parser.hasMoreTokens()) {
249: String childName = parser.nextToken();
250: if (childName.indexOf('[') > -1) {
251: String propName = childName.substring(0, childName
252: .indexOf('['));
253: int i = Integer.parseInt(childName.substring(childName
254: .indexOf('[') + 1, childName.indexOf(']')));
255: child = findNodeByName(child, propName, i, create);
256: } else {
257: child = findNodeByName(child, childName, 0, create);
258: }
259: if (child == null) {
260: break;
261: }
262: }
263:
264: return child;
265: }
266:
267: private Element findNodeByName(Element element, String name,
268: int index, boolean create) {
269: Element prop = null;
270:
271: // Find named property element
272: NodeList propNodes = element.getElementsByTagName(name);
273: if (propNodes.getLength() > index) {
274: prop = (Element) propNodes.item(index);
275: } else {
276: if (create) {
277: for (int i = 0; i < index + 1; i++) {
278: prop = element.getOwnerDocument().createElement(
279: name);
280: element.appendChild(prop);
281: }
282: }
283: }
284: return prop;
285: }
286:
287: /**
288: * Converts a DOM node to a complete xml string
289: * @param node - the node to process
290: * @param indent - how to indent the children of the node
291: * @return The node as a String
292: */
293: public static String nodeToString(Node node, String indent) {
294: StringWriter stringWriter = new StringWriter();
295: PrintWriter printWriter = new PrintWriter(stringWriter);
296:
297: switch (node.getNodeType()) {
298:
299: case Node.DOCUMENT_NODE:
300: printWriter.println("<xml version=\"1.0\">\n");
301: // recurse on each child
302: NodeList nodes = node.getChildNodes();
303: if (nodes != null) {
304: for (int i = 0; i < nodes.getLength(); i++) {
305: printWriter.print(nodeToString(nodes.item(i), ""));
306: }
307: }
308: break;
309:
310: case Node.ELEMENT_NODE:
311: String name = node.getNodeName();
312: printWriter.print(indent + "<" + name);
313: NamedNodeMap attributes = node.getAttributes();
314: for (int i = 0; i < attributes.getLength(); i++) {
315: Node current = attributes.item(i);
316: printWriter.print(" " + current.getNodeName() + "=\""
317: + current.getNodeValue() + "\"");
318: }
319: printWriter.print(">");
320:
321: // recurse on each child
322: NodeList children = node.getChildNodes();
323: if (children != null) {
324: for (int i = 0; i < children.getLength(); i++) {
325: printWriter.print(nodeToString(children.item(i),
326: indent + indent));
327: }
328: }
329:
330: printWriter.print("</" + name + ">");
331: break;
332:
333: case Node.TEXT_NODE:
334: printWriter.print(node.getNodeValue());
335: break;
336: }
337:
338: printWriter.flush();
339: String result = stringWriter.getBuffer().toString();
340: printWriter.close();
341:
342: return result;
343: }
344:
345: }
|