001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019: package org.openharmonise.rm.resources.xml;
020:
021: import java.io.*;
022: import java.util.Vector;
023:
024: import javax.xml.parsers.*;
025:
026: import org.openharmonise.commons.dsi.*;
027: import org.openharmonise.commons.dsi.dml.JoinConditions;
028: import org.openharmonise.commons.xml.XMLDocument;
029: import org.openharmonise.commons.xml.namespace.NamespaceType;
030: import org.openharmonise.rm.*;
031: import org.openharmonise.rm.factory.*;
032: import org.openharmonise.rm.publishing.Publishable;
033: import org.openharmonise.rm.resources.content.TextResource;
034: import org.openharmonise.rm.resources.lifecycle.*;
035: import org.w3c.dom.*;
036: import org.xml.sax.*;
037:
038: /**
039: * This class represnts a generic XML resource within Harmonise.
040: *
041: * @author Michael Bell
042: * @version $Revision: 1.2.2.1 $
043: *
044: */
045: public class XMLResource extends TextResource implements Editable,
046: Publishable, Cloneable, Comparable, EditEventListener {
047:
048: //DB constants
049: private static final String ATTRIB_HREF = "href";
050:
051: private static final String TBL_XML = "xml";
052:
053: //XML constants
054: public static final String TAG_XML = "XML";
055:
056: public static final String PROTOCOL_OH = "ohrm";
057:
058: private XMLDocument m_cached_xincludedDoc = null;
059:
060: private XMLDocument m_cached_doc = null;
061:
062: // initialise content type
063: {
064: m_sContentType = "text/xml";
065: }
066:
067: /**
068: * Basic constructor.
069: */
070: public XMLResource() {
071: super ();
072:
073: }
074:
075: /**
076: * Basic constructor which sets up an interface to the DB.
077: *
078: * @param dbintrf
079: */
080: public XMLResource(AbstractDataStoreInterface dbintrf) {
081: super (dbintrf);
082:
083: }
084:
085: /**
086: * Standard constructor for a known XML resource which may be a historical
087: * version.
088: *
089: * @param dbintrf
090: * @param nId
091: * @param bIsHist
092: */
093: public XMLResource(AbstractDataStoreInterface dbintrf, int nId,
094: int nKey, boolean bIsHist) {
095: super (dbintrf, nId, nKey, bIsHist);
096: }
097:
098: /**
099: * Standard constructor for a known XML resource.
100: *
101: * @param dbintrf
102: * @param nId
103: */
104: public XMLResource(AbstractDataStoreInterface dbintrf, int nId) {
105: super (dbintrf, nId);
106:
107: }
108:
109: /* (non-Javadoc)
110: * @see org.openharmonise.rm.resources.AbstractChildObject#getParentObjectClassName()
111: */
112: public String getParentObjectClassName() {
113:
114: return XMLResourceGroup.class.getName();
115: }
116:
117: /* (non-Javadoc)
118: * @see org.openharmonise.rm.dsi.DataStoreObject#getDBTableName()
119: */
120: public String getDBTableName() {
121: return TBL_XML;
122: }
123:
124: /* (non-Javadoc)
125: * @see org.openharmonise.rm.publishing.Publishable#getTagName()
126: */
127: public String getTagName() {
128: return TAG_XML;
129: }
130:
131: /* (non-Javadoc)
132: * @see org.openharmonise.rm.dsi.DataStoreObject#getInstanceJoinConditions(java.lang.String, boolean)
133: */
134: public JoinConditions getInstanceJoinConditions(String sObjectTag,
135: boolean bIsOuter) throws DataStoreException {
136: return null;
137: }
138:
139: /**
140: * Returns a <code>XMLDocument</code> for this resource.
141: *
142: * @return @throws
143: * DataAccessException
144: */
145: public XMLDocument getDocument() throws DataAccessException {
146:
147: if (m_cached_doc == null) {
148:
149: String sContent = getContent();
150: if (sContent != null) {
151: try {
152: StringReader sreader = new StringReader(sContent);
153:
154: InputSource insource = new InputSource(sreader);
155:
156: DocumentBuilderFactory factory = DocumentBuilderFactory
157: .newInstance();
158:
159: factory.setNamespaceAware(true);
160:
161: DocumentBuilder builder = factory
162: .newDocumentBuilder();
163:
164: m_cached_doc = new XMLDocument(builder
165: .parse(insource));
166: } catch (ParserConfigurationException e) {
167: throw new DataAccessException(
168: "Parser config error", e);
169: } catch (FactoryConfigurationError e) {
170: throw new DataAccessException(
171: "Factory config error", e);
172: } catch (SAXException e) {
173: throw new DataAccessException("SAX error", e);
174: } catch (IOException e) {
175: throw new DataAccessException(
176: "Error occured reading string", e);
177: }
178: } else {
179: m_cached_doc = new XMLDocument();
180: }
181: }
182:
183: //clone Document to avoid problems with tampered docs
184: XMLDocument rDoc = new XMLDocument((Document) m_cached_doc
185: .cloneNode(true));
186:
187: return rDoc;
188: }
189:
190: /**
191: * Returns a <code>XMLDocument</code> with any XInclude tags replaced with
192: * the relevant data.
193: *
194: * @return @throws
195: * DataAccessException
196: */
197: synchronized public XMLDocument getXIncludeResolvedDocument()
198: throws DataAccessException {
199:
200: if (m_cached_xincludedDoc == null) {
201: m_cached_xincludedDoc = new XMLDocument(
202: (org.w3c.dom.Document) getDocument()
203: .cloneNode(true));
204:
205: try {
206: Element docElement = m_cached_xincludedDoc
207: .getDocumentElement();
208:
209: NodeList nodes = docElement.getElementsByTagNameNS(
210: NamespaceType.XINCLUDE.getURI(), "include");
211: Vector replaceList = new Vector();
212: for (int i = 0; i < nodes.getLength(); i++) {
213: replaceList.add((Element) nodes.item(i));
214: }
215: for (int i = 0; i < replaceList.size(); i++) {
216: Element includeEl = (Element) replaceList.get(i);
217: Element replaceEl = getIncludeReplacement(
218: m_cached_xincludedDoc, includeEl);
219: m_cached_xincludedDoc.getDocumentElement()
220: .replaceChild(replaceEl, includeEl);
221: }
222: } catch (NullPointerException npe) {
223: throw new DataAccessException(
224: "No document element in xml resource "
225: + this .m_nId);
226: }
227:
228: }
229:
230: //clone Document to avoid problems with tampered docs
231: XMLDocument rDoc = new XMLDocument(
232: (Document) m_cached_xincludedDoc.cloneNode(true));
233:
234: return rDoc;
235: }
236:
237: /*----------------------------------------------------------------------------
238: Protected methods
239: -----------------------------------------------------------------------------*/
240:
241: /**
242: * Returns the Element which the given include Element represents.
243: *
244: * @param includeEl
245: * @return
246: */
247: protected Element getIncludeReplacement(org.w3c.dom.Document xdoc,
248: Element includeEl) throws DataAccessException {
249:
250: String href = includeEl.getAttribute(ATTRIB_HREF);
251: String sProtocol = PROTOCOL_OH;
252:
253: Element resultEl = null;
254:
255: int nColon = href.indexOf(":");
256:
257: if (nColon >= 0) {
258: sProtocol = href.substring(0, nColon);
259: href = href.substring(nColon);
260: }
261:
262: if (sProtocol.equalsIgnoreCase(PROTOCOL_OH) == true) {
263: try {
264: if (href.startsWith("/") == false) {
265: href = getPath() + href;
266: }
267: String sClassName = this .getClass().getName();
268:
269: XMLResource xmlRes = (XMLResource) HarmoniseObjectFactory
270: .instantiateHarmoniseObject(this .m_dsi,
271: sClassName, href);
272:
273: if (xmlRes == null
274: && sClassName.equals(XMLResource.class
275: .getName()) == false) {
276: xmlRes = (XMLResource) HarmoniseObjectFactory
277: .instantiateHarmoniseObject(this .m_dsi,
278: XMLResource.class.getName(), href);
279: }
280:
281: //add this as a listener so we can clear cached doc if
282: //necessary
283: xmlRes.addEditEventListener(this );
284:
285: resultEl = (Element) xdoc.importNode(xmlRes
286: .getDocument().getDocumentElement(), true);
287:
288: } catch (HarmoniseFactoryException e) {
289: throw new DataAccessException(
290: "Error occured getting xml resource from factory",
291: e);
292: } catch (NullPointerException npe) {
293: throw new DataAccessException(
294: "Error occured getting xml resource from factory",
295: npe);
296: }
297:
298: } else {
299: // TODO support other protocols
300: resultEl = xdoc.createElement("UnsupportedProtocol");
301: }
302:
303: return resultEl;
304: }
305:
306: /* (non-Javadoc)
307: * @see org.openharmonise.rm.resources.AbstractEditableObject#saveNonCoreData()
308: */
309: protected void saveNonCoreData() throws EditException {
310: // nothing to do
311:
312: }
313:
314: /* (non-Javadoc)
315: * @see org.openharmonise.rm.resources.AbstractObject#setIsChanged(boolean)
316: */
317: public void setIsChanged(boolean bIsChanged) {
318: //if something has changed then clear the cached docs
319: if (bIsChanged == true) {
320: m_cached_doc = null;
321: m_cached_xincludedDoc = null;
322: }
323:
324: super .setIsChanged(bIsChanged);
325: }
326:
327: /* (non-Javadoc)
328: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectSaved(org.openharmonise.rm.resources.lifecycle.EditEvent)
329: */
330: public void workflowObjectSaved(EditEvent event) {
331: super .workflowObjectSaved(event);
332:
333: }
334:
335: /* (non-Javadoc)
336: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectStatusChanged(org.openharmonise.rm.resources.lifecycle.EditEvent)
337: */
338: public void workflowObjectStatusChanged(EditEvent event) {
339: super .workflowObjectStatusChanged(event);
340:
341: }
342:
343: /* (non-Javadoc)
344: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectArchived(org.openharmonise.rm.resources.lifecycle.EditEvent)
345: */
346: public void workflowObjectArchived(EditEvent event) {
347: if (event.getSource() instanceof XMLResource) {
348: //if source was a XMLResource it was probably an include
349: //so we need to clear the cached xincluded doc
350: m_cached_xincludedDoc = null;
351: }
352: super .workflowObjectArchived(event);
353: }
354:
355: /* (non-Javadoc)
356: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectReactivated(org.openharmonise.rm.resources.lifecycle.EditEvent)
357: */
358: public void workflowObjectReactivated(EditEvent event) {
359: super .workflowObjectReactivated(event);
360:
361: }
362:
363: /* (non-Javadoc)
364: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectLocked(org.openharmonise.rm.resources.lifecycle.EditEvent)
365: */
366: public void workflowObjectLocked(EditEvent event) {
367: super .workflowObjectLocked(event);
368:
369: }
370:
371: /* (non-Javadoc)
372: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectUnlocked(org.openharmonise.rm.resources.lifecycle.EditEvent)
373: */
374: public void workflowObjectUnlocked(EditEvent event) {
375: super .workflowObjectUnlocked(event);
376:
377: }
378:
379: /* (non-Javadoc)
380: * @see org.openharmonise.rm.resources.content.TextResource#setContent(java.lang.String)
381: */
382: public void setContent(String sContent) throws PopulateException {
383:
384: // ensure that sContent is parseable XML
385: try {
386: StringReader sreader = new StringReader(sContent);
387:
388: InputSource insource = new InputSource(sreader);
389:
390: DocumentBuilderFactory factory = DocumentBuilderFactory
391: .newInstance();
392:
393: factory.setNamespaceAware(true);
394:
395: DocumentBuilder builder = factory.newDocumentBuilder();
396: m_cached_doc = new XMLDocument(builder.parse(insource));
397: m_cached_xincludedDoc = null;
398: } catch (ParserConfigurationException e) {
399: throw new InvalidXMLContentException(e);
400: } catch (SAXException e) {
401: throw new InvalidXMLContentException(e);
402: } catch (IOException e) {
403: throw new InvalidXMLContentException(e);
404: }
405:
406: super.setContent(sContent);
407: }
408:
409: }
|