001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.xml.test;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.ByteArrayOutputStream;
020: import java.util.logging.Logger;
021:
022: import javax.xml.namespace.QName;
023: import javax.xml.parsers.DocumentBuilderFactory;
024: import javax.xml.transform.OutputKeys;
025: import javax.xml.transform.Transformer;
026: import javax.xml.transform.TransformerFactory;
027: import javax.xml.transform.dom.DOMSource;
028: import javax.xml.transform.stream.StreamResult;
029:
030: import junit.framework.TestCase;
031:
032: import org.eclipse.xsd.XSDSchema;
033: import org.geotools.xml.Binding;
034: import org.geotools.xml.Configuration;
035: import org.geotools.xml.DOMParser;
036: import org.geotools.xml.Encoder;
037: import org.geotools.xml.impl.BindingFactoryImpl;
038: import org.geotools.xml.impl.BindingLoader;
039: import org.geotools.xml.impl.BindingWalkerFactoryImpl;
040: import org.picocontainer.MutablePicoContainer;
041: import org.picocontainer.defaults.DefaultPicoContainer;
042: import org.w3c.dom.Document;
043: import org.w3c.dom.Element;
044: import org.w3c.dom.Node;
045:
046: /**
047: * Abstract test class to be used to unit test bindings.
048: * <p>
049: * Subclasses must implement the {@link #createConfiguration()} method. It must
050: * return a new instance of {@link Configuration}. Example:
051: *
052: * <pre>
053: * <code>
054: * public MailTypeBindingTest extends XMLTestSupport {
055: *
056: * protected Configuration createConfiguration() {
057: * return new MLConfiguration();
058: * }
059: * }
060: * </code>
061: * </pre>
062: *
063: * </p>
064: * <p>
065: * The {@link #parse()} method is used to test binding parsing. Subclasses
066: * should call this from test methods after building up an instance document
067: * with {@link #document}. Example
068: *
069: * <pre>
070: * <code>
071: * public void testParsing() throws Exception {
072: * //build up an instance document
073: *
074: * //the root element
075: * Element mailElement = document.createElementNS( ML.NAMESPACE, "mail" );
076: * document.appendChild( mailElement );
077: *
078: * mailElement.setAttribute( "id", "someId" );
079: * ....
080: *
081: * //call parse
082: * Mail mail = (Mail) parse();
083: *
084: * //make assertions
085: * assertEquals( "someId", mail.getId() );
086: * }
087: * </code>
088: * </pre>
089: * </p>
090: *
091: * <p>
092: * The {@link #encode(Object, QName)} method is used to test binding encoding.
093: * Subclasses should call this method from test methods after creating an
094: * object to be encoded. Example:
095: *
096: * <pre>
097: * <code>
098: * public void testEncoding() throws Exception {
099: * //create the mail object
100: * Mail mail = new Mail( "someId" );
101: * mail.setEnvelope( ... );
102: * ....
103: *
104: * //call encode
105: * Document document = encode( mail, new QName( ML.NAMESPACE, "mail" );
106: *
107: * //make assertions
108: * assertEquals( "mail", document.getDocumentElement().getNodeName() );
109: * assertEquals( "someId", document.getDocumentElement().getAttribute( "id" ) );
110: * }
111: * </code>
112: * </pre>
113: * </p>
114: *
115: * <p>
116: * The {@link #binding(QName)} method is used to obtain an instance of a
117: * particular binding. Subclasses should call this method to assert other
118: * properties of the binding, such as type mapping and execution mode. Example:
119: *
120: * <pre>
121: * <code>
122: * public void testType() {
123: * //get an instance of the binding
124: * Binding binding = binding( new QName( ML.NAMESPACE, "MailType" ) );
125: *
126: * //make assertions
127: * assertEquals( Mail.class, binding.getType() );
128: * }
129: *
130: * public void testExecutionMode() {
131: * //get an instance of the binding
132: * Binding binding = binding( new QName( ML.NAMESPACE, "MailType" ) );
133: *
134: * //make assertions
135: * assertEquals( Binding.OVERRIDE, binding.getExecutionMode() );
136: * }
137: * </code>
138: * </pre>
139: * </p>
140: *
141: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
142: *
143: */
144: public abstract class XMLTestSupport extends TestCase {
145: /**
146: * Logging instance
147: */
148: protected static Logger logger = org.geotools.util.logging.Logging
149: .getLogger("org.geotools.xml.test");
150:
151: /**
152: * the instance document
153: */
154: protected Document document;
155:
156: /**
157: * Creates an empty xml document.
158: */
159: protected void setUp() throws Exception {
160: DocumentBuilderFactory docFactory = DocumentBuilderFactory
161: .newInstance();
162: docFactory.setNamespaceAware(true);
163:
164: document = docFactory.newDocumentBuilder().newDocument();
165:
166: }
167:
168: /**
169: * Template method for subclasses to register namespace mappings on the
170: * root element of an instance document.
171: * <p>
172: * Namespace mappings should be set as follows:
173: * <pre>
174: * <code>
175: * root.setAttribute( "xmlns:gml", http://www.opengis.net/gml" );
176: * </code>
177: * </pre>
178: * </p>
179: * <p>
180: * Subclasses of this method should register the default namespace, the
181: * default namesapce is the one returned by the configuration.
182: * </p>
183: * <p>
184: * This method is intended to be extended or overiden. This implementation
185: * registers the <code>xsi,http://www.w3.org/2001/XMLSchema-instance</code>
186: * namespace.
187: * </p>
188: * @param root The root node of the instance document.
189: *
190: */
191: protected void registerNamespaces(Element root) {
192: root.setAttribute("xmlns:xsi",
193: "http://www.w3.org/2001/XMLSchema-instance");
194: }
195:
196: /**
197: * Tempalte method for subclasses to create the configuration to be used by
198: * the parser.
199: *
200: * @return A parser configuration.
201: */
202: protected abstract Configuration createConfiguration();
203:
204: /**
205: * Parses the built document.
206: * <p>
207: * This method should be called after building the entire document.
208: *
209: * </p>
210: * @throws Exception
211: */
212: protected Object parse() throws Exception {
213:
214: Element root = document.getDocumentElement();
215: if (root == null) {
216: throw new IllegalStateException(
217: "Document has no root element");
218: }
219:
220: Configuration config = createConfiguration();
221:
222: registerNamespaces(root);
223:
224: //default
225: root.setAttribute("xsi:schemaLocation", config
226: .getNamespaceURI()
227: + " " + config.getSchemaFileURL());
228:
229: DOMParser parser = new DOMParser(config, document);
230:
231: return parser.parse();
232: }
233:
234: /**
235: * Encodes an object, element name pair.
236: *
237: * @param object The object to encode.
238: * @param element The name of the element to encode.
239: *
240: * @return The object encoded.
241: * @throws Exception
242: */
243: protected Document encode(Object object, QName element)
244: throws Exception {
245: Configuration configuration = createConfiguration();
246: XSDSchema schema = configuration.getSchemaLocator()
247: .locateSchema(null, configuration.getNamespaceURI(),
248: null, null);
249:
250: Encoder encoder = new Encoder(configuration, schema);
251: ByteArrayOutputStream output = new ByteArrayOutputStream();
252: encoder.write(object, element, output);
253:
254: DocumentBuilderFactory dbf = DocumentBuilderFactory
255: .newInstance();
256: dbf.setNamespaceAware(true);
257:
258: return dbf.newDocumentBuilder().parse(
259: new ByteArrayInputStream(output.toByteArray()));
260: }
261:
262: /**
263: * Convenience method to dump the contents of the document to stdout.
264: *
265: *
266: */
267: protected void print(Node dom) throws Exception {
268: TransformerFactory txFactory = TransformerFactory.newInstance();
269: Transformer tx = txFactory.newTransformer();
270: tx.setOutputProperty(OutputKeys.INDENT, "yes");
271:
272: tx.transform(new DOMSource(dom), new StreamResult(System.out));
273: }
274:
275: /**
276: * Convenience method for obtaining an instance of a binding.
277: *
278: * @param name The qualified name of the element,attribute,or type the
279: * binding "binds" to, the key of the binding in the container.
280: *
281: * @return The binding.
282: */
283: protected Binding binding(QName name) {
284:
285: Configuration configuration = createConfiguration();
286:
287: //create the context
288: MutablePicoContainer context = new DefaultPicoContainer();
289: context = configuration.setupContext(context);
290:
291: //create the binding container
292: BindingLoader bindingLoader = new BindingLoader();
293: MutablePicoContainer container = bindingLoader.getContainer();
294: container = configuration.setupBindings(container);
295: bindingLoader.setContainer(container);
296:
297: //register cmponents available to bindings at runtime
298: context.registerComponentInstance(new BindingFactoryImpl(
299: bindingLoader));
300:
301: //binding walker support
302: context.registerComponentInstance(new BindingWalkerFactoryImpl(
303: bindingLoader, context));
304:
305: //logger
306: context.registerComponentInstance(logger);
307:
308: return bindingLoader.loadBinding(name, context);
309: }
310: }
|