001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.ejb.cmp3.xml;
038:
039: import java.io.InputStream;
040: import java.io.IOException;
041:
042: import java.net.URL;
043:
044: import java.util.ArrayList;
045: import java.util.List;
046:
047: import javax.xml.parsers.DocumentBuilder;
048: import javax.xml.parsers.DocumentBuilderFactory;
049: import javax.xml.parsers.ParserConfigurationException;
050:
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Node;
053: import org.w3c.dom.NodeList;
054:
055: import org.xml.sax.SAXException;
056:
057: import oracle.toplink.essentials.exceptions.XMLParseException;
058: import oracle.toplink.essentials.exceptions.ValidationException;
059:
060: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataConstants;
061: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataHelper;
062: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataLogger;
063:
064: import oracle.toplink.essentials.internal.ejb.cmp3.xml.parser.XPathEngine;
065: import oracle.toplink.essentials.internal.ejb.cmp3.xml.parser.XMLException;
066: import oracle.toplink.essentials.internal.ejb.cmp3.xml.parser.XMLExceptionHandler;
067:
068: /**
069: * Utility class used for handling element inspection.
070: *
071: * @author Guy Pelletier
072: * @since TopLink EJB 3.0 Reference Implementation
073: */
074: public class XMLHelper {
075: private Document m_document;
076: private ClassLoader m_loader;
077: private String m_documentName;
078: private String m_defaultPackage;
079: private XPathEngine m_xPathEngine;
080:
081: /**
082: * INTERNAL:
083: */
084: protected XMLHelper(Document document, ClassLoader loader) {
085: m_xPathEngine = XPathEngine.getInstance();
086: m_loader = loader;
087: m_document = document;
088:
089: Node node = getNode(document, new String[] {
090: XMLConstants.ENTITY_MAPPINGS, XMLConstants.PACKAGE,
091: XMLConstants.TEXT });
092:
093: if (node != null && node.getNodeValue() != null) {
094: m_defaultPackage = node.getNodeValue();
095: } else {
096: m_defaultPackage = "";
097: }
098: }
099:
100: /**
101: * INTERNAL:
102: */
103: public XMLHelper(Document document, String fileName,
104: ClassLoader loader) {
105: this (document, loader);
106: m_documentName = fileName;
107: }
108:
109: /**
110: * INTERNAL:
111: */
112: public List<String> getCascadeTypes(Node node) {
113: ArrayList<String> cTypes = new ArrayList<String>();
114:
115: NodeList cascadeTypes = getNodes(node, XMLConstants.CASCADE,
116: XMLConstants.ALL_CHILDREN);
117: for (int i = 0; i < cascadeTypes.getLength(); i++) {
118: cTypes.add(cascadeTypes.item(i).getLocalName());
119: }
120:
121: return cTypes;
122: }
123:
124: /**
125: * INTERNAL:
126: */
127: public Class getClassForName(String className) {
128: return MetadataHelper.getClassForName(
129: getFullyQualifiedClassName(className), m_loader);
130: }
131:
132: /**
133: * INTERNAL:
134: * Return the Class for a given node. This method assumes that the node
135: * requires a class attribute, i.e. entity or mapped-superclass.
136: */
137: public Class getClassForNode(Node node) {
138: return MetadataHelper.getClassForName(
139: getClassNameForNode(node), m_loader);
140: }
141:
142: /**
143: * INTERNAL:
144: * Return the fully qualified class name for a given node. This method
145: * assumes that the node requires a class attribute, i.e. entity or
146: * mapped-superclass.
147: */
148: public String getClassNameForNode(Node node) {
149: return getFullyQualifiedClassName(getNodeValue(node,
150: XMLConstants.ATT_CLASS));
151: }
152:
153: /**
154: * INTERNAL:
155: * Return the instance document associated with this helper.
156: */
157: public Document getDocument() {
158: return m_document;
159: }
160:
161: /**
162: * INTERNAL:
163: * Return the instance document name associated with this helper.
164: */
165: public String getDocumentName() {
166: return m_documentName;
167: }
168:
169: /**
170: * INTERNAL:
171: */
172: public String getFetchTypeDefaultEAGER(Node node) {
173: return getNodeValue(node, XMLConstants.ATT_FETCH,
174: MetadataConstants.EAGER);
175: }
176:
177: /**
178: * INTERNAL:
179: */
180: public String getFetchTypeDefaultLAZY(Node node) {
181: return getNodeValue(node, XMLConstants.ATT_FETCH,
182: MetadataConstants.LAZY);
183: }
184:
185: /**
186: * INTERNAL:
187: * This convenience method will attempt to fully qualify a class name if
188: * required. This assumes that the className value is non-null, and a
189: * "qualified" class name contains at least one '.'
190: */
191: public String getFullyQualifiedClassName(String className) {
192: return getFullyQualifiedClassName(className, m_defaultPackage);
193: }
194:
195: /**
196: * INTERNAL:
197: * This convenience method will attempt to fully qualify a class name if
198: * required. This assumes that the className value is non-null, and a
199: * "qualified" class name contains at least one '.'
200: */
201: public static String getFullyQualifiedClassName(String className,
202: String packageName) {
203: // if there is no global package defined or the class name is qualified, return className
204: if (packageName.equals("") || className.indexOf(".") != -1) {
205: return className;
206: }
207:
208: // prepend the package to the class name
209: // format of global package is "foo.bar."
210: if (packageName.endsWith(".")) {
211: return (packageName + className);
212: }
213:
214: // format of global package is "foo.bar"
215: return (packageName + "." + className);
216: }
217:
218: /**
219: * INTERNAL:
220: * This convenience method determines the type of relationship mapping the
221: * node represents, and returns the appropriate logging context.
222: */
223: public String getLoggingContextForDefaultMappingReferenceClass(
224: Node mappingNode) {
225: if (mappingNode.getLocalName().equals(XMLConstants.ONE_TO_ONE)) {
226: return MetadataLogger.ONE_TO_ONE_MAPPING_REFERENCE_CLASS;
227: }
228: if (mappingNode.getLocalName().equals(XMLConstants.ONE_TO_MANY)) {
229: return MetadataLogger.ONE_TO_MANY_MAPPING_REFERENCE_CLASS;
230: }
231: if (mappingNode.getLocalName().equals(XMLConstants.MANY_TO_ONE)) {
232: return MetadataLogger.MANY_TO_ONE_MAPPING_REFERENCE_CLASS;
233: }
234: // assume many-to-many
235: return MetadataLogger.MANY_TO_MANY_MAPPING_REFERENCE_CLASS;
236: }
237:
238: /**
239: * INTERNAL:
240: */
241: public String getMappedBy(Node node) {
242: return getNodeValue(node, XMLConstants.ATT_MAPPED_BY, "");
243: }
244:
245: /**
246: * INTERNAL:
247: * Get a node off the given node.
248: */
249: public Node getNode(Node node, String xPath) {
250: return getNode(node, new String[] { xPath });
251: }
252:
253: /**
254: * INTERNAL:
255: * Get a node off the given node.
256: */
257: public Node getNode(Node node, String[] xPath) {
258: return m_xPathEngine.selectSingleNode(node, xPath);
259: }
260:
261: /**
262: * INTERNAL:
263: * Get a node off the document node.
264: */
265: public Node getNode(String[] xPath) {
266: return getNode(m_document, xPath);
267: }
268:
269: /**
270: * INTERNAL:
271: * Get the nodes off the given node.
272: */
273: public NodeList getNodes(String xPath1, String xPath2) {
274: return getNodes(m_document, new String[] { xPath1, xPath2 });
275: }
276:
277: /**
278: * INTERNAL:
279: * Get the nodes off the given node.
280: */
281: public NodeList getNodes(String[] xPath) {
282: return getNodes(m_document, xPath);
283: }
284:
285: /**
286: * INTERNAL:
287: */
288: public String getNodeTextValue(Node node, String xPath) {
289: return getNodeValue(node, new String[] { xPath,
290: XMLConstants.TEXT });
291: }
292:
293: /**
294: * INTERNAL:
295: */
296: public String getNodeTextValue(String xPath1, String xPath2) {
297: return getNodeValue(m_document, new String[] { xPath1, xPath2,
298: XMLConstants.TEXT });
299: }
300:
301: /**
302: * INTERNAL:
303: */
304: public String getNodeTextValue(String xPath1, String xPath2,
305: String defaultValue) {
306: return getNodeValue(m_document, new String[] { xPath1, xPath2,
307: XMLConstants.TEXT }, defaultValue);
308: }
309:
310: /**
311: * INTERNAL:
312: */
313: public String getNodeValue(Node node, String xPath) {
314: return getNodeValue(node, new String[] { xPath });
315: }
316:
317: /**
318: * INTERNAL:
319: */
320: public boolean getNodeValue(Node node, String xPath,
321: boolean defaultValue) {
322: return getNodeValue(node, new String[] { xPath }, defaultValue);
323: }
324:
325: /**
326: * INTERNAL:
327: */
328: public Class getNodeValue(Node node, String xPath,
329: Class defaultValue) {
330: return getNodeValue(node, new String[] { xPath }, defaultValue);
331: }
332:
333: /**
334: * INTERNAL:
335: */
336: public int getNodeValue(Node node, String xPath, int defaultValue) {
337: return getNodeValue(node, new String[] { xPath }, defaultValue);
338: }
339:
340: /**
341: * INTERNAL:
342: */
343: public String getNodeValue(Node node, String xPath,
344: String defaultValue) {
345: return getNodeValue(node, new String[] { xPath }, defaultValue);
346: }
347:
348: /**
349: * INTERNAL:
350: */
351: public NodeList getNodes(Node node, String xPath) {
352: return getNodes(node, new String[] { xPath });
353: }
354:
355: /**
356: * INTERNAL:
357: */
358: public NodeList getNodes(Node node, String xPath1, String xPath2) {
359: return getNodes(node, new String[] { xPath1, xPath2 });
360: }
361:
362: /**
363: * INTERNAL:
364: */
365: public NodeList getNodes(Node node, String[] xPath) {
366: return m_xPathEngine.selectNodes(node, xPath);
367: }
368:
369: /**
370: * INTERNAL:
371: */
372: public boolean getNodeValue(Node node, String[] xPath,
373: boolean defaultValue) {
374: return getValue(getNode(node, xPath), defaultValue);
375: }
376:
377: /**
378: * INTERNAL:
379: */
380: public Class getNodeValue(Node node, String[] xPath,
381: Class defaultValue) {
382: return getValue(getNode(node, xPath), defaultValue);
383: }
384:
385: /**
386: * INTERNAL:
387: */
388: public int getNodeValue(Node node, String[] xPath, int defaultValue) {
389: return getValue(getNode(node, xPath), defaultValue);
390: }
391:
392: /**
393: * INTERNAL:
394: */
395: public String getNodeValue(Node node, String[] xPath,
396: String defaultValue) {
397: return getValue(getNode(node, xPath), defaultValue);
398: }
399:
400: /**
401: * INTERNAL:
402: */
403: public String getNodeValue(Node node, String[] xPath) {
404: return getNodeValue(node, xPath, "");
405: }
406:
407: /**
408: * INTERNAL:
409: */
410: public String getNodeValue(String[] xPath) {
411: return getNodeValue(xPath, "");
412: }
413:
414: /**
415: * INTERNAL:
416: */
417: public int getNodeValue(String[] xPath, int defaultValue) {
418: return getValue(getNode(xPath), defaultValue);
419: }
420:
421: /**
422: * INTERNAL:
423: */
424: public String getNodeValue(String[] xPath, String defaultValue) {
425: return getValue(getNode(xPath), defaultValue);
426: }
427:
428: /**
429: * INTERNAL:
430: */
431: public String getPackage() {
432: return m_defaultPackage;
433: }
434:
435: /**
436: * INTERNAL:
437: */
438: public Class getTargetEntity(Node node) {
439: return getNodeValue(node, XMLConstants.ATT_TARGET_ENTITY,
440: void.class);
441: }
442:
443: /**
444: * INTERNAL:
445: */
446: public NodeList getTextColumnNodes(Node node) {
447: return getNodes(node, new String[] { XMLConstants.COLUMN_NAME,
448: XMLConstants.TEXT });
449: }
450:
451: /**
452: * INTERNAL:
453: */
454: private boolean getValue(Node node, boolean defaultValue) {
455: if (node == null) {
456: return defaultValue;
457: } else {
458: return Boolean.parseBoolean(node.getNodeValue());
459: }
460: }
461:
462: /**
463: * INTERNAL:
464: */
465: private Class getValue(Node node, Class defaultValue) {
466: if (node == null) {
467: return defaultValue;
468: } else {
469: return getClassForName(node.getNodeValue());
470: }
471: }
472:
473: /**
474: * INTERNAL:
475: */
476: private int getValue(Node node, int defaultValue) {
477: if (node == null) {
478: return defaultValue;
479: } else {
480: return Integer.parseInt(node.getNodeValue());
481: }
482: }
483:
484: /**
485: * INTERNAL:
486: */
487: private String getValue(Node node, String defaultValue) {
488: if (node == null) {
489: return defaultValue;
490: } else {
491: String value = node.getNodeValue();
492: if (value == null) {
493: return defaultValue;
494: } else {
495: return value;
496: }
497: }
498: }
499:
500: /**
501: * INTERNAL:
502: */
503: public boolean hasNode(Node node, String xPath) {
504: return getNode(node, xPath) != null;
505: }
506:
507: /**
508: * INTERNAL:
509: */
510: public boolean isOptional(Node node) {
511: return getNodeValue(node, XMLConstants.ATT_OPTIONAL, true);
512: }
513:
514: /**
515: * INTERNAL:
516: * Locate a node in the DOM tree for a given class.
517: */
518: public Node locateEmbeddableNode(Class cls) {
519: return locateNode(cls, XMLConstants.EMBEDDABLE);
520: }
521:
522: /**
523: * INTERNAL:
524: * Locate a node in the DOM tree for a given class.
525: */
526: public Node locateEntityNode(Class cls) {
527: return locateNode(cls, XMLConstants.ENTITY);
528: }
529:
530: /**
531: * INTERNAL:
532: * Locate a node in the DOM tree for a given class.
533: */
534: public Node locateMappedSuperclassNode(Class cls) {
535: return locateNode(cls, XMLConstants.MAPPED_SUPERCLASS);
536: }
537:
538: /**
539: * INTERNAL:
540: * Locate a node in the DOM tree for the given class. Will look for
541: * an entity, embeddable, or mapped-superclass node with @class matching
542: * the class name.
543: */
544: public Node locateNode(Class cls) {
545: Node result = null;
546: result = locateEntityNode(cls);
547:
548: if (result == null) {
549: result = locateMappedSuperclassNode(cls);
550: }
551:
552: if (result == null) {
553: result = locateEmbeddableNode(cls);
554: }
555:
556: return result;
557: }
558:
559: /**
560: * INTERNAL:
561: * Locate a node in the DOM tree for a given class.
562: * The search string should be used as follows:
563: * - For an entity: XMLConstants.ENTITY
564: * - For an embeddable: XMLConstants.EMBEDDABLE
565: * - For a mapped superclass: XMLConstants.MAPPED_SUPERCLASS
566: * Or call locateNode which will check them all. For efficiency, it looks
567: * for an entity first.
568: */
569: private Node locateNode(Class cls, String searchString) {
570: NodeList nodes = getNodes(m_document,
571: XMLConstants.ENTITY_MAPPINGS, searchString);
572:
573: if (nodes != null) {
574: for (int i = 0; i < nodes.getLength(); i++) {
575: Node node = nodes.item(i);
576: // process @class (required)
577: if (getClassNameForNode(node).equals(cls.getName())) {
578: return node;
579: }
580: }
581: }
582:
583: return null;
584: }
585:
586: /**
587: * INTERNAL:
588: * Locate a node in the DOM tree for a given attribute name.
589: */
590: // WIP - method may go away.
591: public Node locateNodeForAttribute(Node node, String attributeName) {
592: NodeList attributeNodes = getNodes(node,
593: XMLConstants.ATTRIBUTES, XMLConstants.ALL_CHILDREN);
594:
595: if (attributeNodes != null) {
596: Node attributeNode;
597: for (int i = 0; i < attributeNodes.getLength(); i++) {
598: attributeNode = attributeNodes.item(i);
599: // process @name (required)
600: if (getNodeValue(attributeNode, XMLConstants.ATT_NAME)
601: .equals(attributeName)) {
602: return attributeNode;
603: }
604: }
605: }
606:
607: return null;
608: }
609:
610: /**
611: * INTERNAL:
612: * Return the root entity in an entity class hierarchy
613: */
614: public Class locateRootEntity(Class entityClass) {
615: Class super class = entityClass.getSuperclass();
616: if (super class != null) {
617: Node entityNode = locateEntityNode(super class);
618:
619: if (entityNode != null) {
620: return locateRootEntity(super class);
621: }
622: }
623:
624: return entityClass;
625: }
626:
627: /**
628: * INTERNAL:
629: * Indicates if a given node has a primary-key-join-column sub-element.
630: */
631: public boolean nodeHasPrimaryKeyJoinColumns(Node node) {
632: if (node == null) {
633: return false;
634: }
635:
636: NodeList nodes = getNodes(node, XMLConstants.PK_JOIN_COLUMN);
637: return (nodes != null && nodes.getLength() > 0);
638: }
639:
640: /**
641: * INTERNAL:
642: * Indicates if a given node has a primary-key-join-column sub-element.
643: */
644: public boolean nodeHasJoinColumns(Node node) {
645: if (node == null) {
646: return false;
647: }
648:
649: NodeList nodes = getNodes(node, XMLConstants.JOIN_COLUMN);
650: return (nodes != null && nodes.getLength() > 0);
651: }
652:
653: /**
654: * INTERNAL:
655: * Build a DOM from an instance document using the provided URL.
656: */
657: public static Document parseDocument(
658: InputStream xmlDocumentInputStream, String documentName,
659: ClassLoader loader) {
660: DocumentBuilderFactory dbf = DocumentBuilderFactory
661: .newInstance();
662: dbf.setNamespaceAware(true);
663: dbf.setAttribute(XMLConstants.SCHEMA_LANGUAGE,
664: XMLConstants.XML_SCHEMA);
665: dbf.setValidating(true);
666:
667: // attempt to load the schema from the classpath
668: URL schemaURL = loader
669: .getResource(XMLConstants.ORM_SCHEMA_NAME);
670: if (schemaURL != null) {
671: dbf.setAttribute(XMLConstants.JAXP_SCHEMA_SOURCE, schemaURL
672: .toString());
673: }
674:
675: // create a document builder
676: DocumentBuilder db;
677: try {
678: db = dbf.newDocumentBuilder();
679: } catch (ParserConfigurationException pex) {
680: throw XMLParseException.exceptionCreatingDocumentBuilder(
681: documentName, pex);
682: }
683:
684: // set the parse exception handler
685: XMLExceptionHandler xmlExceptionHandler = new XMLExceptionHandler();
686: db.setErrorHandler(xmlExceptionHandler);
687:
688: // parse the document
689: Document doc = null;
690: try {
691: doc = db.parse(xmlDocumentInputStream);
692: } catch (IOException ioex) {
693: throw XMLParseException.exceptionReadingXMLDocument(
694: documentName, ioex);
695: } catch (SAXException saxex) {
696: // XMLExceptionHandler will handle parse exceptions
697: }
698:
699: XMLException xmlEx = xmlExceptionHandler.getXMLException();
700: if (xmlEx != null) {
701: throw ValidationException.invalidEntityMappingsDocument(
702: documentName, xmlEx);
703: }
704:
705: return doc;
706: }
707:
708: /**
709: * INTERNAL:
710: * Update the loader after it changes.
711: */
712: public void setLoader(ClassLoader loader) {
713: m_loader = loader;
714: }
715:
716: /**
717: * INTERNAL:
718: * Get the loader.
719: */
720: public ClassLoader getClassLoader() {
721: return m_loader;
722: }
723: }
|