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.eclipse.core.runtime.IPath;
22  import org.w3c.dom.Document;
23  import org.xml.sax.SAXException;
24  import org.xml.sax.SAXNotRecognizedException;
25  import org.xml.sax.SAXNotSupportedException;
26  import com.sun.org.apache.xerces.internal.impl.Constants;
27  import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;
28  import com.sun.org.apache.xerces.internal.parsers.DOMParser;
29  import com.sun.org.apache.xerces.internal.xni.Augmentations;
30  import com.sun.org.apache.xerces.internal.xni.XMLString;
31  import com.sun.org.apache.xerces.internal.xni.XNIException;
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 		return new XMLAccess(doc, null, root, fileName);
140 	}
141 	/***
142 	 * Creates <code>XMLAccess</code> instance with empty document.
143 	 * @param root URL pointing to the directory where the document is located.
144 	 * @param fileName the name of the document file, including the extension.
145 	 * @return initialized instance of XMLAccess.
146 	 * @throws DocumentException when error occurs during document processing
147 	 */
148 	public XMLAccess newDocument(URLDir root, String fileName)
149 			throws DocumentException {
150 		// create new empty document
151 		DocumentBuilderFactory factory = new DocumentBuilderFactoryImpl();
152 		DocumentBuilder builder;
153 		try {
154 			builder = factory.newDocumentBuilder();
155 		} catch (Exception ex) {
156 			throw new Error("Unexpected.", ex); //$NON-NLS-1$
157 		}
158 		final Document doc = builder.newDocument();
159 		return new XMLAccess(doc, null, root, fileName);
160 	}
161 	/***
162 	 * Initializes an <code>XMLAccess</code> instance from given DOM document.
163 	 * @param doc document to initialize.
164 	 * @param textEntities all text enities encountered during the parse.
165 	 * @param root URL pointing to the directory where the document is located.
166 	 * @param fileName the name of the document file, including the extension.
167 	 * @return initialized instance of XMLAccess.
168 	 * @throws DocumentException when error occurs during document processing
169 	 */
170 	private XMLAccess initDocument(Document doc,
171 			Map<String, String> textEntities, URLDir root, String fileName)
172 			throws DocumentException {
173 		final XMLAccess result = new XMLAccess(doc, textEntities, root, fileName);
174 		return result;
175 	}
176 	/***
177 	 * We are going to parse the XML via Xerces's XNI methods. This is the
178 	 * instance of DOMParser used for parsing documents. With its help we are
179 	 * going to catch all entity declarations correctly.
180 	 * @author Martin Vysny
181 	 */
182 	private class Parser extends DOMParser {
183 		/***
184 		 * Contains mapping between the entity name and entity value.
185 		 */
186 		public final Map<String, String> textEntities = new HashMap<String, String>();
187 		/***
188 		 * Constructor.
189 		 * @param root directory where the document is located.
190 		 */
191 		public Parser(URLDir root) {
192 			super();
193 			try {
194 				setFeature(Constants.SAX_FEATURE_PREFIX
195 						+ Constants.VALIDATION_FEATURE, false);
196 				// "namespaceAware" == SAX Namespaces feature
197 				setFeature(Constants.SAX_FEATURE_PREFIX
198 						+ Constants.NAMESPACES_FEATURE, true);
199 				// Set various parameters obtained from DocumentBuilderFactory
200 				setFeature(Constants.XERCES_FEATURE_PREFIX
201 						+ Constants.INCLUDE_IGNORABLE_WHITESPACE, false);
202 				setFeature(Constants.XERCES_FEATURE_PREFIX
203 						+ Constants.CREATE_ENTITY_REF_NODES_FEATURE, true);
204 				setFeature(Constants.XERCES_FEATURE_PREFIX
205 						+ Constants.INCLUDE_COMMENTS_FEATURE, true);
206 				setFeature(Constants.XERCES_FEATURE_PREFIX
207 						+ Constants.CREATE_CDATA_NODES_FEATURE, true);
208 				setFeature(Constants.XERCES_FEATURE_PREFIX
209 						+ Constants.LOAD_DTD_GRAMMAR_FEATURE, false);
210 			} catch (SAXNotRecognizedException ex) {
211 				throw new Error(ex);
212 			} catch (SAXNotSupportedException ex) {
213 				throw new Error(ex);
214 			}
215 			setEntityResolver(new EmEntityResolver(root));
216 		}
217 		/*
218 		 * (non-Javadoc)
219 		 * @see org.apache.xerces.parsers.XMLParser#reset()
220 		 */
221 		@Override
222 		public void reset() throws XNIException {
223 			textEntities.clear();
224 			super.reset();
225 		}
226 		/*
227 		 * (non-Javadoc)
228 		 * @see org.apache.xerces.xni.XMLDTDHandler#internalEntityDecl(java.lang.String,
229 		 * org.apache.xerces.xni.XMLString, org.apache.xerces.xni.XMLString,
230 		 * org.apache.xerces.xni.Augmentations)
231 		 */
232 		@Override
233 		public void internalEntityDecl(String name, XMLString text,
234 				XMLString nonNormalizedText, Augmentations augs)
235 				throws XNIException {
236 			if (name.charAt(0) != '%') {
237 				// textual entity. store it into the map. Intern them because
238 				// they may be created multiple times in the document.
239 				textEntities.put(name.intern(), text.toString().intern());
240 			}
241 			super.internalEntityDecl(name, text, nonNormalizedText, augs);
242 		}
243 	}
244 }