View Javadoc

1   /*
2    * Copyright 1999-2006 Faculty of Mathematics, Physics and Informatics, Comenius
3    * University, Bratislava. This file is protected by the Mozilla Public License
4    * version 1.1 (the License); you may not use this file except in compliance
5    * with the License. You may obtain a copy of the License at
6    * http://euromath2.sourceforge.net/license.html Unless required by applicable
7    * law or agreed to in writing, software distributed under the License is
8    * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9    * KIND, either express or implied. See the License for the specific language
10   * governing permissions and limitations under the License.
11   */
12  package sk.uniba.euromath.document;
13  import java.io.IOException;
14  import java.net.MalformedURLException;
15  import java.net.URISyntaxException;
16  import java.net.URL;
17  import java.util.HashMap;
18  import java.util.Map;
19  import javax.xml.parsers.DocumentBuilder;
20  import javax.xml.parsers.DocumentBuilderFactory;
21  import org.apache.xerces.impl.Constants;
22  import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
23  import org.apache.xerces.parsers.DOMParser;
24  import org.apache.xerces.xni.Augmentations;
25  import org.apache.xerces.xni.XMLString;
26  import org.apache.xerces.xni.XNIException;
27  import org.eclipse.core.runtime.IPath;
28  import org.w3c.dom.Document;
29  import org.xml.sax.SAXException;
30  import org.xml.sax.SAXNotRecognizedException;
31  import org.xml.sax.SAXNotSupportedException;
32  import sk.uniba.euromath.tools.URLDir;
33  /***
34   * Deserializes the document.
35   * @author Martin Vysny
36   */
37  public final class DocumentFactory {
38  	/***
39  	 * Constructs the factory.
40  	 */
41  	public DocumentFactory() {
42  		super();
43  	}
44  	/***
45  	 * <p>
46  	 * Loads an XML from specified location and prepares it for editing. The
47  	 * steps that has to be taken (in this order) to load document properly:
48  	 * <ul>
49  	 * <li>The document is loaded into DOM model, of course :)</li>
50  	 * <li>Load schema for every namespace used in the document.</li>
51  	 * <li>add schema-default attributes and/or elements, that weren't in
52  	 * original document, into DOM model and mark them as default</li>
53  	 * <li>assign unique ID for every element and text node</li>
54  	 * <li>load XSLT for every namespace used in the document</li>
55  	 * </ul>
56  	 * </p>
57  	 * <p>
58  	 * These steps should be taken to load document properly:
59  	 * <ol>
60  	 * <li>Obtain an <code>XMLAccess</code> instance.</li>
61  	 * <li>Optional step: Load local schemas with the
62  	 * <code>XMLAccess.loadLocalSchema()</code> function.</li>
63  	 * <li>Call <code>XMLAccess.loadGlobalSchemas()</code> to ensure that for
64  	 * each namespace there is schema loaded.</li>
65  	 * <li>Call <code>XMLAccess.validate()</code> to validate the document.
66  	 * </li>
67  	 * <li>Fill <code>Selector</code> to select transformers for all
68  	 * namespaces.</li>
69  	 * <li>Open view on document via the <code>XMLAccess.openView()</code>
70  	 * function.</li>
71  	 * <li>Get transformed (presentation) document via the
72  	 * <code>XMLAccess.getTransformedReader()</code> reader and display it.
73  	 * </li>
74  	 * </ol>
75  	 * </p>
76  	 * @param resource the eclipse resource representing the xml file.
77  	 * @return XMLAccess representation of document.
78  	 * @throws SAXException when error occurs during document deserialization
79  	 * @throws IOException when i/o error occurs
80  	 * @throws DocumentException when error occurs during document processing
81  	 */
82  	public XMLAccess loadDocument(IPath resource) throws SAXException,
83  			IOException, DocumentException {
84  		return loadDocument(resource.toFile().toURL());
85  	}
86  	/***
87  	 * Loads an XML from specified location and prepares it for editing.
88  	 * @param url the URL address representing the location of the XML document.
89  	 * @return XMLAccess representation of document.
90  	 * @throws SAXException when error occurs during document deserialization
91  	 * @throws IOException when i/o error occurs
92  	 * @throws DocumentException when error occurs during document processing
93  	 */
94  	public XMLAccess loadDocument(String url) throws SAXException, IOException,
95  			DocumentException {
96  		return loadDocument(new URL(url));
97  	}
98  	/***
99  	 * Loads an XML from specified location and prepares it for editing.
100 	 * @param url the URL address representing the location of the XML document.
101 	 * @return XMLAccess representation of document.
102 	 * @throws SAXException when error occurs during document deserialization
103 	 * @throws IOException when i/o error occurs
104 	 * @throws DocumentException when error occurs during document processing
105 	 */
106 	public XMLAccess loadDocument(URL url) throws SAXException, IOException,
107 			DocumentException {
108 		// split the uri to path and filename
109 		String urlStr = url.toString();
110 		int lastSlash = urlStr.lastIndexOf('/');
111 		String fileName = urlStr.substring(lastSlash + 1);
112 		URLDir rootPath;
113 		if (lastSlash < 0) {
114 			rootPath = new URLDir();
115 		} else {
116 			try {
117 				rootPath = new URLDir(urlStr.substring(0, lastSlash));
118 			} catch (MalformedURLException ex) {
119 				throw new Error("Unexpected exception", ex); //$NON-NLS-1$
120 			} catch (URISyntaxException ex) {
121 				throw new Error("Unexpected exception", ex); //$NON-NLS-1$
122 			}
123 		}
124 		Parser parser = new Parser(rootPath);
125 		parser.parse(rootPath.getURI().toString() + fileName);
126 		Document doc = parser.getDocument();
127 		return initDocument(doc, parser.textEntities, rootPath, fileName);
128 	}
129 	/***
130 	 * Initializes an <code>XMLAccess</code> instance from given DOM document.
131 	 * @param doc document to initialize.
132 	 * @param root URL pointing to the directory where the document is located.
133 	 * @param fileName the name of the document file, including the extension.
134 	 * @return initialized instance of XMLAccess.
135 	 * @throws DocumentException when error occurs during document processing
136 	 */
137 	public XMLAccess newDocument(Document doc, URLDir root, String fileName)
138 			throws DocumentException {
139 		DomCore dom = new DomCore(doc, null, root, fileName);
140 		return new XMLAccess(dom);
141 	}
142 	/***
143 	 * Creates <code>XMLAccess</code> instance with empty document.
144 	 * @param root URL pointing to the directory where the document is located.
145 	 * @param fileName the name of the document file, including the extension.
146 	 * @return initialized instance of XMLAccess.
147 	 * @throws DocumentException when error occurs during document processing
148 	 */
149 	public XMLAccess newDocument(URLDir root, String fileName)
150 			throws DocumentException {
151 		// create new empty document
152 		DocumentBuilderFactory factory = new DocumentBuilderFactoryImpl();
153 		DocumentBuilder builder;
154 		try {
155 			builder = factory.newDocumentBuilder();
156 		} catch (Exception ex) {
157 			throw new Error("Unexpected.", ex); //$NON-NLS-1$
158 		}
159 		Document doc = builder.newDocument();
160 		DomCore dom = new DomCore(doc, null, root, fileName);
161 		return new XMLAccess(dom);
162 	}
163 	/***
164 	 * Initializes an <code>XMLAccess</code> instance from given DOM document.
165 	 * @param doc document to initialize.
166 	 * @param textEntities all text enities encountered during the parse.
167 	 * @param root URL pointing to the directory where the document is located.
168 	 * @param fileName the name of the document file, including the extension.
169 	 * @return initialized instance of XMLAccess.
170 	 * @throws DocumentException when error occurs during document processing
171 	 */
172 	private XMLAccess initDocument(Document doc,
173 			Map<String, String> textEntities, URLDir root, String fileName)
174 			throws DocumentException {
175 		final DomCore dom = new DomCore(doc, textEntities, root, fileName);
176 		final XMLAccess result = new XMLAccess(dom);
177 		return result;
178 	}
179 	/***
180 	 * We are going to parse the XML via Xerces's XNI methods. This is the
181 	 * instance of DOMParser used for parsing documents. With its help we are
182 	 * going to catch all entity declarations correctly.
183 	 * @author Martin Vysny
184 	 */
185 	private class Parser extends DOMParser {
186 		/***
187 		 * Contains mapping between the entity name and entity value.
188 		 */
189 		public final Map<String, String> textEntities = new HashMap<String, String>();
190 		/***
191 		 * Constructor.
192 		 * @param root directory where the document is located.
193 		 */
194 		public Parser(URLDir root) {
195 			super();
196 			try {
197 				setFeature(Constants.SAX_FEATURE_PREFIX
198 						+ Constants.VALIDATION_FEATURE, false);
199 				// "namespaceAware" == SAX Namespaces feature
200 				setFeature(Constants.SAX_FEATURE_PREFIX
201 						+ Constants.NAMESPACES_FEATURE, true);
202 				// Set various parameters obtained from DocumentBuilderFactory
203 				setFeature(Constants.XERCES_FEATURE_PREFIX
204 						+ Constants.INCLUDE_IGNORABLE_WHITESPACE, false);
205 				setFeature(Constants.XERCES_FEATURE_PREFIX
206 						+ Constants.CREATE_ENTITY_REF_NODES_FEATURE, true);
207 				setFeature(Constants.XERCES_FEATURE_PREFIX
208 						+ Constants.INCLUDE_COMMENTS_FEATURE, true);
209 				setFeature(Constants.XERCES_FEATURE_PREFIX
210 						+ Constants.CREATE_CDATA_NODES_FEATURE, true);
211 				setFeature(Constants.XERCES_FEATURE_PREFIX
212 						+ Constants.LOAD_DTD_GRAMMAR_FEATURE, false);
213 			} catch (SAXNotRecognizedException ex) {
214 				throw new Error(ex);
215 			} catch (SAXNotSupportedException ex) {
216 				throw new Error(ex);
217 			}
218 			setEntityResolver(new EmEntityResolver(root));
219 		}
220 		/*
221 		 * (non-Javadoc)
222 		 * @see org.apache.xerces.parsers.XMLParser#reset()
223 		 */
224 		@Override
225 		public void reset() throws XNIException {
226 			textEntities.clear();
227 			super.reset();
228 		}
229 		/*
230 		 * (non-Javadoc)
231 		 * @see org.apache.xerces.xni.XMLDTDHandler#internalEntityDecl(java.lang.String,
232 		 * org.apache.xerces.xni.XMLString, org.apache.xerces.xni.XMLString,
233 		 * org.apache.xerces.xni.Augmentations)
234 		 */
235 		@Override
236 		public void internalEntityDecl(String name, XMLString text,
237 				XMLString nonNormalizedText, Augmentations augs)
238 				throws XNIException {
239 			if (name.charAt(0) != '%') {
240 				// textual entity. store it into the map. Intern them because
241 				// they may be created multiple times in the document.
242 				textEntities.put(name.intern(), text.toString().intern());
243 			}
244 			super.internalEntityDecl(name, text, nonNormalizedText, augs);
245 		}
246 	}
247 }