001: /*--------------------------------------------------------------------------*
002: | Copyright (C) 2006 Christopher Kohlhaas |
003: | |
004: | This program is free software; you can redistribute it and/or modify |
005: | it under the terms of the GNU General Public License as published by the |
006: | Free Software Foundation. A copy of the license has been included with |
007: | these distribution in the COPYING file, if not go to www.fsf.org . |
008: | |
009: | As a special exception, you are granted the permissions to link this |
010: | program with every library, which license fulfills the Open Source |
011: | Definition as published by the Open Source Initiative (OSI). |
012: *--------------------------------------------------------------------------*/
013: package org.rapla.storage.xml;
014:
015: import java.io.IOException;
016: import java.io.Reader;
017: import java.io.StringReader;
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.net.URL;
022:
023: import org.apache.avalon.framework.logger.Logger;
024: import org.rapla.framework.RaplaException;
025: import org.xml.sax.ContentHandler;
026: import org.xml.sax.ErrorHandler;
027: import org.xml.sax.InputSource;
028: import org.xml.sax.SAXException;
029: import org.xml.sax.SAXParseException;
030:
031: /** Reads the data in xml format from an InputSource into the
032: LocalCache and converts it to a newer version if necessary.
033: */
034: public final class RaplaInput {
035: private Logger logger;
036: private URL fileSource;
037: private Reader reader;
038:
039: private boolean wasConverted;
040:
041: public RaplaInput(Logger logger) throws RaplaException {
042: this .logger = logger;
043: }
044:
045: protected Logger getLogger() {
046: return logger;
047: }
048:
049: /** returns if the data was converted during read.*/
050: public boolean wasConverted() {
051: return wasConverted;
052: }
053:
054: public void read(URL file, ContentHandler handler, boolean validate)
055: throws RaplaException, IOException {
056: getLogger().debug("Parsing " + file.toString());
057: fileSource = file;
058: reader = null;
059: parseData(handler, validate);
060: }
061:
062: public boolean read(Reader xml, ContentHandler handler,
063: boolean validate) throws RaplaException, IOException {
064: fileSource = null;
065: reader = xml;
066: parseData(handler, validate);
067: return wasConverted;
068: }
069:
070: public boolean read(Reader xml, ContentHandler handler)
071: throws RaplaException, IOException {
072: fileSource = null;
073: reader = xml;
074: parseData(handler, false);
075: return wasConverted;
076: }
077:
078: public boolean readWithNamespaces(String xml, ContentHandler handler)
079: throws RaplaException, IOException {
080: StringBuffer dataElement = new StringBuffer();
081: dataElement.append("<rapla:data ");
082: for (int i = 0; i < RaplaXMLWriter.NAMESPACE_ARRAY.length; i++) {
083: String prefix = RaplaXMLWriter.NAMESPACE_ARRAY[i][1];
084: String uri = RaplaXMLWriter.NAMESPACE_ARRAY[i][0];
085: if (prefix == null) {
086: dataElement.append("xmlns=");
087: } else {
088: dataElement.append("xmlns:" + prefix + "=");
089: }
090: dataElement.append("\"");
091: dataElement.append(uri);
092: dataElement.append("\" ");
093: }
094: dataElement.append(">");
095: String xmlWithNamespaces = dataElement.toString() + xml
096: + "</rapla:data>";
097: return read(new StringReader(xmlWithNamespaces), handler);
098: }
099:
100: private InputSource getNewSource() {
101: if (fileSource != null) {
102: return new InputSource(fileSource.toString());
103: } else if (reader != null) {
104: return new InputSource(reader);
105: } else {
106: throw new IllegalStateException(
107: "fileSource or reader can't be null");
108: }
109: }
110:
111: private void parseData(ContentHandler contentHandler,
112: boolean validate) throws RaplaException, IOException {
113: try {
114: RaplaSAXPipeline pipeline = new RaplaSAXPipeline();
115: pipeline.enableLogging(getLogger());
116: if (validate) {
117: validate(getNewSource(),
118: "org/rapla/storage/xml/rapla.rng");
119: }
120: pipeline.parse(contentHandler, getNewSource());
121: } catch (SAXException ex) {
122: Throwable cause = ex.getException();
123: if (cause instanceof SAXParseException) {
124: ex = (SAXParseException) cause;
125: cause = ex.getException();
126: }
127: if (ex instanceof SAXParseException) {
128: throw new RaplaException("Line: "
129: + ((SAXParseException) ex).getLineNumber()
130: + " Column: "
131: + ((SAXParseException) ex).getColumnNumber()
132: + " "
133: + ((cause != null) ? cause.getMessage() : ex
134: .getMessage()), (cause != null) ? cause
135: : ex);
136: }
137: if (cause == null) {
138: throw new RaplaException(ex);
139: }
140: if (cause instanceof WrongVersionException) {
141: convertData(getNewSource(), contentHandler,
142: ((WrongVersionException) cause).getVersion());
143: return;
144: }
145: if (cause instanceof RaplaException)
146: throw (RaplaException) cause;
147: else
148: throw new RaplaException(cause);
149: }
150: /* End of Exception Handling */
151: }
152:
153: /** uses the jing validator to validate a document against an relaxng schema.
154: * This method uses reflection API, to avoid compile-time dependencies on
155: * the jing.jar
156: * @param in
157: * @param schema
158: * @throws RaplaException
159: */
160: private void validate(InputSource in, String schema)
161: throws RaplaException {
162: try {
163: ErrorHandler errorHandler = new RaplaErrorHandler();
164: /* // short version
165: * propMapBuilder = new com.thaiopensource.util.PropertyMapBuilder();
166: * propMapBuilder.put(com.thaiopensource.validate.ValidateProperty.ERROR_HANDLER, errorHandler);
167: * Object propMap = propMapBuilder.toPropertyMap();
168: * Object o =new com.thaiopensource.validate.ValidationDriver(propMap);
169: * o.loadSchema(schema);
170: * o.validate(in);
171: */
172: // full reflection syntax
173: Class validatorC = Class
174: .forName("com.thaiopensource.validate.ValidationDriver");
175: Class propIdC = Class
176: .forName("com.thaiopensource.util.PropertyId");
177: Class validatepropC = Class
178: .forName("com.thaiopensource.validate.ValidateProperty");
179: Object errorHandlerId = validatepropC.getDeclaredField(
180: "ERROR_HANDLER").get(null);
181: Class propMapC = Class
182: .forName("com.thaiopensource.util.PropertyMap");
183: Class propMapBuilderC = Class
184: .forName("com.thaiopensource.util.PropertyMapBuilder");
185: Object propMapBuilder = propMapBuilderC.newInstance();
186: Method put = propMapBuilderC.getMethod("put", new Class[] {
187: propIdC, Object.class });
188: put.invoke(propMapBuilder, new Object[] { errorHandlerId,
189: errorHandler });
190: Method topropMap = propMapBuilderC.getMethod(
191: "toPropertyMap", new Class[] {});
192: Object propMap = topropMap.invoke(propMapBuilder,
193: new Object[] {});
194: Constructor validatorConst = validatorC
195: .getConstructor(new Class[] { propMapC });
196: Object validator = validatorConst
197: .newInstance(new Object[] { propMap });
198: Method loadSchema = validatorC.getMethod("loadSchema",
199: new Class[] { InputSource.class });
200: Method validate = validatorC.getMethod("validate",
201: new Class[] { InputSource.class });
202: InputSource schemaSource = new InputSource(getResource(
203: schema).toString());
204: loadSchema.invoke(validator, new Object[] { schemaSource });
205: validate.invoke(validator, new Object[] { in });
206: } catch (ClassNotFoundException ex) {
207: throw new RaplaException(
208: ex.getMessage()
209: + ". Latest jing.jar is missing on the classpath. Please download from http://www.thaiopensource.com/relaxng/jing.html");
210: } catch (InvocationTargetException e) {
211: throw new RaplaException(
212: "Can't validate data due to the following error: "
213: + e.getTargetException().getMessage(), e
214: .getTargetException());
215: } catch (Exception ex) {
216: throw new RaplaException("Error invoking JING", ex);
217: }
218: }
219:
220: private URL getResource(String name) throws RaplaException {
221: URL url = getClass().getClassLoader().getResource(name);
222: if (url == null)
223: throw new RaplaException("Resource " + name + " not found");
224: return url;
225: }
226:
227: private void convertData(InputSource inputSource,
228: ContentHandler handler, String versionString)
229: throws RaplaException, IOException {
230: double version;
231: try {
232: version = new Double(versionString).doubleValue();
233: } catch (NumberFormatException ex) {
234: throw new RaplaException(
235: "Invalid version tag (double-value expected)!");
236: }
237: // get the version number of the data-schema
238: if (version > new Double(RaplaMainReader.INPUT_FILE_VERSION)
239: .doubleValue())
240: throw new RaplaException(
241: "This version of Rapla cannot read files with a version-number"
242: + " greater than "
243: + RaplaMainReader.INPUT_FILE_VERSION
244: + ", try out the latest version.");
245:
246: try {
247: RaplaSAXPipeline pipeline = new RaplaSAXPipeline();
248: pipeline.enableLogging(getLogger());
249: if (version < 0.4) {
250: throw new RaplaException(
251: "Rapla 0.7, 0.6 or rapla 0.5 files are not supported in this version\n"
252: + " Please use rapla version 0.8.2 to convert this file: Load file, edit and save something!");
253: }
254: if (version < 0.5) {
255: URL stylesheet = getResource("org/rapla/storage/xml/convert0_4to0_5.xsl");
256: pipeline.addTransformer(stylesheet, new String[][] {});
257: }
258: if (version < 0.6) {
259: URL stylesheet = getResource("org/rapla/storage/xml/convert0_5to0_6.xsl");
260: pipeline.addTransformer(stylesheet, new String[][] {});
261: }
262: if (version < 0.7) {
263: URL stylesheet = getResource("org/rapla/storage/xml/convert0_6to0_7.xsl");
264: pipeline.addTransformer(stylesheet, new String[][] {});
265: }
266: if (version < 0.8) {
267: URL stylesheet = getResource("org/rapla/storage/xml/convert0_7to0_8.xsl");
268: pipeline.addTransformer(stylesheet, new String[][] {});
269: }
270: if (version < 0.9) {
271: URL stylesheet = getResource("org/rapla/storage/xml/convert0_8to0_9.xsl");
272: pipeline.addTransformer(stylesheet, new String[][] {});
273: }
274:
275: if (version < 1.0) {
276: URL stylesheet = getResource("org/rapla/storage/xml/convert0_9to1_0.xsl");
277: pipeline.addTransformer(stylesheet, new String[][] {});
278: }
279:
280: getLogger().info("Start conversion");
281: //pipeline.parse(new DefaultHandler(), inputSource);
282:
283: pipeline.parse(handler, inputSource);
284: getLogger().info("Conversion successful");
285: wasConverted = true;
286: } catch (SAXException ex) {
287: Throwable cause = ex.getException();
288: if (cause == null)
289: throw new RaplaException(ex);
290:
291: if (cause instanceof RaplaException) {
292: throw (RaplaException) cause;
293: } else {
294: throw new RaplaException(cause);
295: }
296: }
297: }
298:
299: }
|