001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.schema2beans;
043:
044: import java.util.*;
045: import java.io.*;
046:
047: import org.w3c.dom.*;
048: import org.xml.sax.*;
049:
050: import javax.xml.parsers.DocumentBuilder;
051: import javax.xml.parsers.DocumentBuilderFactory;
052:
053: /**
054: *
055: *
056: */
057: public class GraphManager extends Object {
058:
059: public static interface Writer {
060: public void write(OutputStream out, Document doc);
061: }
062:
063: public static interface Factory {
064: public org.w3c.dom.Document createDocument(InputStream in,
065: boolean validate);
066: }
067:
068: Document document = null;
069: NodeFactory factory = null;
070: HashMap bindingsMap = new HashMap();
071: BaseBean root;
072: private boolean writeCData = false;
073:
074: // When set to null (default), use XMLDocument instead
075: private Factory docFactory;
076: private Writer docWriter;
077:
078: private String docTypePublic;
079: private String docTypeSystem;
080:
081: //
082: // The key is the input stream. This is how we can get the
083: // factory/writer when we are asked to build a Dom graph.
084: //
085: static Map factoryMap = Collections.synchronizedMap(new HashMap(2));
086: static Map writerMap = Collections.synchronizedMap(new HashMap(2));
087:
088: public GraphManager(BaseBean root) {
089: this .root = root;
090: }
091:
092: /**
093: * Associate a factory to a stream
094: */
095: public static void setFactory(InputStream in,
096: GraphManager.Factory factory) throws Schema2BeansException {
097: setFactory(in, factory, null);
098: }
099:
100: /**
101: * Set an external factory to use instead of the default one
102: */
103: public static void setFactory(InputStream in,
104: GraphManager.Factory factory, GraphManager.Writer writer)
105: throws Schema2BeansException {
106: if (in == null)
107: throw new Schema2BeansException(Common
108: .getMessage("InputStreamCantBeNull_msg"));
109:
110: if (factory != null)
111: GraphManager.factoryMap.put(in, factory);
112: else
113: GraphManager.factoryMap.remove(in);
114:
115: if (writer != null)
116: GraphManager.writerMap.put(in, writer);
117: else
118: GraphManager.writerMap.remove(in);
119: }
120:
121: /**
122: * Set an external writer to use instead of the default one
123: */
124: public void setWriter(GraphManager.Writer writer) {
125: this .docWriter = writer;
126: }
127:
128: public void setWriteCData(boolean value) {
129: writeCData = value;
130: }
131:
132: public static Node createRootElementNode(String name)
133: throws Schema2BeansRuntimeException {
134: String s = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" + // NOI18N
135: "<" + name + "/>"; // NOI18N
136:
137: ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
138: Document doc = GraphManager.createXmlDocument(in, false);
139: NodeList children = doc.getChildNodes();
140: int length = children.getLength();
141: for (int i = 0; i < length; ++i) {
142: Node node = children.item(i);
143: if (node instanceof DocumentType) {
144: //System.out.println("Found DocumentType where there should be none.");
145: doc.removeChild(node);
146: --length;
147: }
148: }
149: return doc;
150: }
151:
152: //
153: // Document created for this GraphManager. Called by the generated bean.
154: //
155: public void setXmlDocument(Node doc)
156: throws Schema2BeansRuntimeException {
157: if (doc instanceof Document) {
158: this .document = (Document) doc;
159: this .setNodeFactory((Document) doc);
160:
161: //
162: // The factory/writer should know about the doc now
163: // and no more about the original InputStream.
164: // (if the user specified a factory/writer)
165: //
166: Object o = GraphManager.factoryMap.get(doc);
167: if (o != null) {
168: this .docFactory = (GraphManager.Factory) o;
169: GraphManager.factoryMap.remove(doc);
170: }
171:
172: o = GraphManager.writerMap.get(doc);
173: if (o != null) {
174: this .docWriter = (GraphManager.Writer) o;
175: GraphManager.writerMap.remove(doc);
176: }
177: } else
178: throw new Schema2BeansRuntimeException(Common
179: .getMessage("CantFindFactory_msg"));
180: }
181:
182: /**
183: * This returns the DOM Document object, root
184: * of the current DOM graph. Operations that cause structural
185: * modifications to the DOM graph are not allowed. Indeed,
186: * modifying the DOM graph directly would cause the bean graph
187: * and its internal representation to be out of sync.
188: */
189: public Document getXmlDocument() {
190: return this .document;
191: }
192:
193: public void setDoctype(String publicId, String systemId) {
194: //System.out.println("GraphManager.setDoctype: publicId="+publicId+" systemId="+systemId);
195: this .docTypePublic = publicId;
196: this .docTypeSystem = systemId;
197: }
198:
199: /**
200: * Parse the DOM tree until the element named 'name' is found.
201: * Return the node of the name or null if not found.
202: * This method is used by the root bean generated class to get
203: * the root element of the DOM tree and start building the
204: * bean graph from here.
205: */
206: public static Node getElementNode(String name, Node doc) {
207: Node n;
208: for (n = doc.getFirstChild(); n != null; n = n.getNextSibling()) {
209: if (n.getNodeType() == Node.ELEMENT_NODE
210: && n.getNodeName().equals(name)) {
211: break;
212: }
213: }
214: return n;
215: }
216:
217: /**
218: * This method is called by the createRoot() method of the root bean
219: * (part of the BaseBean class). The doc might not be available
220: * at the time of this call. In such a case, the method
221: * completeRootBinding is called afterwards with the doc value to complete
222: * the setup of the root.
223: *
224: * This makes sure that the root element of the object bindings
225: * between the beans and the DOM Nodes is created, before that the
226: * recursing creation of the graph begins.
227: */
228: public void createRootBinding(BaseBean beanRoot, BeanProp prop,
229: Node doc) throws Schema2BeansException {
230: prop.registerDomNode(doc, null, beanRoot);
231: if (doc != null)
232: this .bindingsMap.put(doc, beanRoot.binding);
233: }
234:
235: public void completeRootBinding(BaseBean beanRoot, Node doc) {
236: this .bindingsMap.put(doc, beanRoot.binding);
237: beanRoot.binding.setNode(doc);
238: }
239:
240: /**
241: * This method sets the DOM nodes factory.
242: */
243: public void setNodeFactory(Document doc) {
244: this .factory = new NodeFactory(doc);
245: }
246:
247: /**
248: * Return the DOM node factory
249: */
250: public NodeFactory getNodeFactory() {
251: return this .factory;
252: }
253:
254: /**
255: * Return the root of the bean graph
256: */
257: public BaseBean getBeanRoot() {
258: return this .root;
259: }
260:
261: /**
262: * OutputStream version of write()
263: */
264: void write(OutputStream out) throws IOException,
265: Schema2BeansException {
266: //
267: // Code specific to the DOM implementation:
268: //
269: if (this .document == null)
270: throw new Schema2BeansException(Common
271: .getMessage("CantGetDocument_msg"));
272: if (this .docWriter != null)
273: this .docWriter.write(out, this .document);
274: else {
275: XMLUtil.DOMWriter domWriter = getDOMWriter();
276: domWriter.write(out, document);
277: }
278: }
279:
280: protected void write(OutputStream out, String encoding)
281: throws java.io.IOException {
282: XMLUtil.DOMWriter domWriter = getDOMWriter();
283: domWriter.write(out, encoding, document);
284: }
285:
286: protected void write(java.io.Writer out) throws java.io.IOException {
287: XMLUtil.DOMWriter domWriter = getDOMWriter();
288: domWriter.setWriter(out);
289: domWriter.write(document);
290: }
291:
292: protected void write(java.io.Writer out, String encoding)
293: throws java.io.IOException {
294: XMLUtil.DOMWriter domWriter = getDOMWriter();
295: domWriter.setWriter(out);
296: domWriter.write(document, encoding);
297: }
298:
299: public void write(java.io.Writer out, Node node)
300: throws java.io.IOException, Schema2BeansException {
301: XMLUtil.DOMWriter domWriter = getDOMWriter();
302: domWriter.setWriter(out);
303: domWriter.write(node);
304: }
305:
306: protected XMLUtil.DOMWriter getDOMWriter() {
307: XMLUtil.DOMWriter domWriter = new XMLUtil.DOMWriter();
308: domWriter.setDocTypePublic(docTypePublic);
309: domWriter.setDocTypeSystem(docTypeSystem);
310: domWriter.setWriteCData(writeCData);
311: return domWriter;
312: }
313:
314: /**
315: * Take the current DOM tree and readjust whitespace so that it
316: * looks pretty.
317: */
318: public void reindent(String indent) {
319: XMLUtil.reindent(document, indent);
320: }
321:
322: /**
323: * Indent by 2 spaces for every @level.
324: */
325: protected static void printLevel(java.io.Writer out, int level,
326: String indent) throws java.io.IOException {
327: StringBuffer outBuf = new StringBuffer();
328: printLevel(outBuf, level, indent);
329: out.write(outBuf.toString());
330: }
331:
332: protected static void printLevel(StringBuffer out, int level,
333: String indent) {
334: for (int i = 0; i < level; ++i) {
335: out.append(indent);
336: }
337: }
338:
339: protected static void printLevel(java.io.Writer out, int level,
340: String indent, String text) throws java.io.IOException {
341: StringBuffer outBuf = new StringBuffer();
342: printLevel(outBuf, level, indent, text);
343: out.write(outBuf.toString());
344: }
345:
346: protected static void printLevel(OutputStream out, int level,
347: String indent, String text) throws java.io.IOException {
348: OutputStreamWriter w = new OutputStreamWriter(out);
349: printLevel(w, level, indent, text);
350: w.flush();
351: }
352:
353: protected static void printLevel(StringBuffer out, int level,
354: String indent, String text) {
355: printLevel(out, level, indent);
356: out.append(text);
357: }
358:
359: /**
360: * Creates a DOM document from the input stream.
361: */
362: public static Document createXmlDocument(InputStream in,
363: boolean validate) throws Schema2BeansRuntimeException {
364: return createXmlDocument(in, validate, null);
365: }
366:
367: private static InputStream tee(InputStream in) throws IOException {
368: byte[] buf = new byte[4096];
369: ByteArrayOutputStream ba = new ByteArrayOutputStream();
370: int totalLength = 0;
371: int len;
372: while ((len = in.read(buf, 0, 4096)) > 0) {
373: ba.write(buf, 0, len);
374: totalLength += len;
375: }
376: System.out.println("schema2beans: in (length=" + totalLength
377: + "):");
378: System.out.println(ba.toString());
379: ByteArrayInputStream bain = new ByteArrayInputStream(ba
380: .toByteArray());
381: return bain;
382: }
383:
384: /**
385: * Creates a DOM document from the input stream.
386: */
387: public static Document createXmlDocument(InputStream in,
388: boolean validate, EntityResolver er)
389: throws Schema2BeansRuntimeException {
390: if (in == null)
391: throw new IllegalArgumentException("in == null"); // NOI18N
392: try {
393: if (DDLogFlags.debug) {
394: // Dump the contents to stdout
395: in = tee(in);
396: }
397:
398: //
399: // Change the references to map the newly created doc
400: // The BaseBean instance is not created yet. The doc
401: // document will be used to get back the factories.
402: //
403: Object o = GraphManager.factoryMap.get(in);
404: if (o != null) {
405: GraphManager.Factory f = (GraphManager.Factory) o;
406:
407: Document doc = f.createDocument(in, validate);
408:
409: GraphManager.factoryMap.remove(in);
410: GraphManager.factoryMap.put(doc, o);
411:
412: Object o2 = GraphManager.writerMap.get(in);
413: if (o2 != null) {
414: GraphManager.writerMap.remove(in);
415: GraphManager.writerMap.put(doc, o2);
416: }
417: return doc;
418: } else {
419: return createXmlDocument(new InputSource(in), validate,
420: er, null);
421: }
422: } catch (Schema2BeansException e) {
423: throw new Schema2BeansRuntimeException(e);
424: } catch (IOException e) {
425: throw new Schema2BeansRuntimeException(e);
426: }
427: }
428:
429: public static Document createXmlDocument(InputSource in,
430: boolean validate) throws Schema2BeansException {
431: return createXmlDocument(in, validate, null, null);
432: }
433:
434: public static Document createXmlDocument(InputSource in,
435: boolean validate, EntityResolver er, ErrorHandler eh)
436: throws Schema2BeansException {
437: if (in == null)
438: throw new IllegalArgumentException("in == null"); // NOI18N
439: if (validate == false && er == null) {
440: // The client is not interested in any validation, so make
441: // see to it that any entity resolution doesn't hit the network
442: er = NullEntityResolver.newInstance();
443: }
444: try {
445: // Build a Document using JAXP
446: DocumentBuilderFactory dbf = DocumentBuilderFactory
447: .newInstance();
448: dbf.setValidating(validate);
449:
450: DocumentBuilder db = dbf.newDocumentBuilder();
451: if (er != null)
452: db.setEntityResolver(er);
453: if (eh != null)
454: db.setErrorHandler(eh);
455:
456: if (DDLogFlags.debug) {
457: System.out.println("createXmlDocument: validate="
458: + validate + " dbf=" + dbf + " db=" + db
459: + " er=" + er);
460: }
461:
462: return db.parse(in);
463: } catch (javax.xml.parsers.ParserConfigurationException e) {
464: throw new Schema2BeansNestedException(Common
465: .getMessage("CantCreateXMLDOMDocument_msg"), e);
466: } catch (org.xml.sax.SAXException e) {
467: throw new Schema2BeansNestedException(Common
468: .getMessage("CantCreateXMLDOMDocument_msg"), e);
469: } catch (IOException e) {
470: throw new Schema2BeansNestedException(Common
471: .getMessage("CantCreateXMLDOMDocument_msg"), e);
472: }
473: }
474:
475: /**
476: * This method is called by the generated beans when they are
477: * building themselves from a DOM tree.
478: * Typically, the first root bean calls this method with the
479: * DOM root node and the list of the properties that are expected
480: * under this node.
481: * This method parses the DOM sub-node of the node and matches their names
482: * with the names of the properties. When a match is found, the
483: * bean property object is called with the node found. If the node
484: * has no match in the bean properties, the node is ignored but
485: * the event is logged as it might reveal a problem in the bean tree
486: * (DTD element missing in the bean class graph).
487: *
488: */
489: public void fillProperties(BeanProp[] prop, Node node)
490: throws Schema2BeansException {
491: BaseBean bean;
492: DOMBinding binding, newBinding;
493:
494: if (prop == null || node == null)
495: return;
496:
497: if (this .bindingsMap.get(node) == null) {
498: throw new Schema2BeansException(Common.getMessage(
499: "CurrentNodeHasNoBinding_msg", new Integer(node
500: .hashCode())));
501: }
502:
503: // Store the property's dtdName's into a map for fast lookup,
504: // and be able to handle multiple properties with the same name.
505: Map dtdName2Prop = new HashMap(); // Map<String, BeanProp>
506: Map dupDtdNames = new HashMap(); // Map<String, List<BeanProp>>
507: for (int i = 0; i < prop.length; i++) {
508: String dtdName = prop[i].dtdName;
509: if (dtdName2Prop.containsKey(dtdName)) {
510: //System.out.println("Found duplicate dtdName="+dtdName);
511: List dupList = (List) dupDtdNames.get(dtdName);
512: if (dupList == null) {
513: dupList = new ArrayList();
514: //dupList.add(dtdName2Prop.get(dtdName));
515: dupDtdNames.put(dtdName, dupList);
516: }
517: dupList.add(prop[i]);
518: } else {
519: dtdName2Prop.put(dtdName, prop[i]);
520: }
521: }
522:
523: // Assume that the DOM parsing takes longer than prop parsing
524: Map dupDtdNameIterators = new HashMap(); // Map<String, Iterator<BeanProp>>
525: for (Node n = node.getFirstChild(); n != null; n = n
526: .getNextSibling()) {
527: if (n.getNodeType() == Node.ELEMENT_NODE) {
528: String eltName = n.getNodeName();
529:
530: //System.out.println("eltName="+eltName);
531: BeanProp matchingProp = (BeanProp) dtdName2Prop
532: .get(eltName);
533: if (matchingProp != null) {
534: List dupList = (List) dupDtdNames.get(eltName);
535: if (dupList != null) {
536: // There are mutliple BeanProp's with the same dtd name,
537: // figure out which one we should pick.
538: if (!Common.isArray(matchingProp.type)) {
539: Iterator propIt = (Iterator) dupDtdNameIterators
540: .get(eltName);
541: //System.out.println("propIt="+propIt);
542: if (propIt == null) {
543: // First time, let the matchingProp load it,
544: // but set it up for next time.
545: propIt = dupList.iterator();
546: dupDtdNameIterators
547: .put(eltName, propIt);
548: } else if (propIt.hasNext()) {
549: matchingProp = (BeanProp) propIt.next();
550: }
551: }
552: }
553: binding = (DOMBinding) this .bindingsMap.get(n);
554:
555: if (DDLogFlags.debug) {
556: String s = eltName
557: + " N("
558: + n.hashCode()
559: + ") - "
560: + (binding == null ? "new node"
561: : "already bound B("
562: + binding.hashCode()
563: + ")");
564: TraceLogger.put(TraceLogger.DEBUG,
565: TraceLogger.SVC_DD, DDLogFlags.DBG_BLD,
566: 1, DDLogFlags.FOUNDNODE, s);
567: }
568:
569: newBinding = matchingProp.registerDomNode(n,
570: binding, null);
571:
572: if (newBinding != null) {
573:
574: if (Common.isBean(matchingProp.type))
575: bean = (BaseBean) newBinding
576: .getBean(matchingProp);
577: else
578: bean = null;
579:
580: if (DDLogFlags.debug) {
581: String s = "B("
582: + newBinding.hashCode()
583: + ") - "
584: + matchingProp.getPropClass()
585: .getName();
586: TraceLogger.put(TraceLogger.DEBUG,
587: TraceLogger.SVC_DD,
588: DDLogFlags.DBG_BLD, 1,
589: DDLogFlags.BOUNDNODE, s);
590: }
591:
592: if (bean != null) {
593: //
594: // The property was a bean, fill up this bean.
595: // This is were the recursing call in the
596: // creation of the bean graph happens.
597: //
598: if (binding == null)
599: this .bindingsMap.put(n, newBinding);
600: bean.createBean(n, this );
601: }
602: }
603: } else {
604: // log that there is no matching
605: if (DDLogFlags.debug) {
606: TraceLogger.put(TraceLogger.DEBUG,
607: TraceLogger.SVC_DD, DDLogFlags.DBG_BLD,
608: 1, DDLogFlags.NONODE, eltName);
609: }
610: }
611: } else {
612: // Log that this is not an element
613: short t = n.getNodeType();
614: String v = n.getNodeValue();
615: if (DDLogFlags.debug) {
616: TraceLogger.put(TraceLogger.DEBUG,
617: TraceLogger.SVC_DD, DDLogFlags.DBG_BLD, 1,
618: DDLogFlags.NOTELT, DDFactory
619: .typeToString(t)
620: + " = " + Common.dumpHex(v));
621: }
622: }
623: }
624: }
625:
626: //////////////////////
627: //
628: // Event misc. methods, base on the name of the PropertyChanged event
629: //
630: // BaseBean getPropertyParent(String name)
631: // String getPropertyParentName(String name)
632: // String getPropertyName(String name)
633: // int getPropertyIndex(String name)
634: // String getAttributeName(String name)
635: // boolean isAttribute(String name)
636: //
637:
638: /**
639: * Return the bean holding the property 'name' as a BaseBean object
640: */
641: public BaseBean getPropertyParent(String name) {
642: return (BaseBean) getPropertyParent(root, name);
643: }
644:
645: public static Bean getPropertyParent(Bean theRoot, String name) {
646: String[] path = name.split("/", -1); // NOI18N
647: int n = path.length;
648: if (n < 2 || path[0].length() > 0) {
649: throw new IllegalArgumentException(Common.getMessage(
650: "NameShouldStartWithSlash_msg", name));
651: }
652: if (n == 2) {
653: return null;
654: }
655: Bean curBean = theRoot;
656: for (int i = 2; i < n - 1; i++) {
657: String[] element = path[i].split("[.]", 2); // NOI18N
658: String beanName = element[0];
659: int index;
660: if (element.length == 1) {
661: index = 0;
662: } else {
663: String indexName = element[1];
664: if (indexName.indexOf('i') != -1) {
665: throw new IllegalStateException(Common.getMessage(
666: "CantFindBeanBecausePartOfNameRemoved_msg",
667: beanName, name));
668: }
669: index = Integer.parseInt(indexName, 16);
670: }
671: curBean = curBean.propertyById(beanName, index);
672: if (curBean == null) {
673: throw new IllegalStateException(Common.getMessage(
674: "CantFindBeanMayHaveBeenRemoved_msg", beanName,
675: name));
676: }
677: }
678: return curBean;
679: }
680:
681: public String getKeyPropertyName(String propName, String[] prop,
682: String[] key) {
683: return this .getKeyPropertyName(propName, prop, key, false);
684: }
685:
686: public String getKeyPropertyName(String propName) {
687: return this .getKeyPropertyName(propName, null, null, true);
688: }
689:
690: /**
691: * Return the bean holding the property 'name' as a BaseBean object
692: */
693: public String getKeyPropertyName(String propName, String[] prop,
694: String[] key, boolean keyName) {
695: return getKeyPropertyName(root, propName, prop, key, keyName);
696: }
697:
698: public static String getKeyPropertyName(Bean theRoot,
699: String propName, String[] prop, String[] key,
700: boolean keyName) {
701:
702: StringBuffer keyPropName = new StringBuffer();
703: Bean curBean = theRoot;
704: String beanName, indexName;
705: String name = propName;
706:
707: if (name.charAt(0) == '/')
708: name = name.substring(1);
709:
710: do {
711: int i = name.indexOf('/');
712: if (i != -1) {
713: beanName = name.substring(0, i);
714: name = name.substring(i + 1);
715: } else {
716: beanName = name;
717: name = null;
718: }
719:
720: i = beanName.indexOf('.');
721:
722: if (i != -1) {
723: indexName = beanName.substring(i + 1);
724: beanName = beanName.substring(0, i);
725:
726: if (indexName.indexOf('i') != -1)
727: throw new IllegalStateException(Common.getMessage(
728: "CantFindBeanBecausePartOfNameRemoved_msg",
729: beanName, propName));
730: } else
731: indexName = "0"; // NOI18N
732:
733: if (theRoot.hasName(beanName)) {
734: curBean = theRoot;
735: } else {
736: if (curBean.getProperty(beanName).isBean()) {
737: curBean = curBean.propertyById(beanName, Integer
738: .parseInt(indexName, 16));
739: } else
740: curBean = null;
741: }
742:
743: keyPropName.append(beanName);
744:
745: if (prop != null && curBean != null) {
746: // If a property name/key is defined, add it to the path
747: for (i = 0; i < prop.length; i++) {
748: if (prop[i].equals(beanName)) {
749: keyPropName.append("."); // NOI18N
750: keyPropName.append(key[i]);
751: keyPropName.append("="); // NOI18N
752: String v = (String) curBean.getValue(key[i], 0);
753: if (v != null)
754: keyPropName.append(v);
755: break;
756: }
757: }
758: } else if (keyName && curBean != null) {
759: // If any property has 'name', use it
760: BaseProperty[] l = curBean.listProperties();
761: for (i = 0; i < l.length; i++) {
762: String n = l[i].getName();
763: if (n.toLowerCase().indexOf("name") != -1) { // NOI18N
764: keyPropName.append("."); // NOI18N
765: keyPropName.append(n);
766: keyPropName.append("="); // NOI18N
767: String v = (String) curBean.getValue(n, 0);
768: if (v != null)
769: keyPropName.append(v);
770: break;
771: }
772: }
773: }
774:
775: if (name != null)
776: keyPropName.append("/"); // NOI18N
777:
778: } while (name != null && curBean != null);
779:
780: return keyPropName.toString();
781: }
782:
783: /**
784: * Return the bean holding the property 'name' as a BaseBean object
785: */
786: public static String trimPropertyName(String propName) {
787: StringBuffer name = new StringBuffer();
788: int i, j;
789: i = 0;
790: do {
791: j = propName.indexOf('.', i);
792: if (j == -1) {
793: name.append(propName.substring(i));
794: } else {
795: name.append(propName.substring(i, j));
796: i = propName.indexOf('/', j);
797: }
798: } while (j != -1 && i != -1);
799:
800: return name.toString();
801: }
802:
803: /**
804: * Return the name of the bean holding the property 'name'
805: */
806: public static String getPropertyParentName(String name) {
807: int i = name.lastIndexOf('/');
808: if (i != -1)
809: name = name.substring(0, i);
810: i = name.lastIndexOf('/');
811: if (i != -1)
812: name = name.substring(i + 1);
813: i = name.lastIndexOf('.');
814: if (i != -1)
815: name = name.substring(0, i);
816:
817: return name;
818: }
819:
820: /**
821: * Return the name of the property of the PropertyChangeEvent named name.
822: * Any index or attribute is removed from the name of the event.
823: *
824: * single property: /Book/Chapter.2/Comment -> Comment
825: * indexed property: /Book/Chapter.4 -> Chapter
826: * attribute: /Book/Chapter.2:title -> Chapter
827: *
828: */
829: public static String getPropertyName(String name) {
830: int i = name.lastIndexOf('/');
831: if (i != -1)
832: name = name.substring(i + 1);
833: // Remove the index value
834: i = name.lastIndexOf('.');
835: if (i != -1)
836: name = name.substring(0, i);
837: // If there is a still an attribute, remove it
838: i = name.lastIndexOf(':');
839: if (i != -1)
840: name = name.substring(0, i);
841:
842: return name;
843: }
844:
845: /**
846: * Return the name of the attribute if this is the name of an attribute,
847: * return null otherwise.
848: *
849: * single property: /Book/Chapter.2/Comment -> null
850: * indexed property: /Book/Chapter.4 -> null
851: * attribute: /Book/Chapter.2:title -> title
852: *
853: */
854: public String getAttributeName(String name) {
855: int i = name.lastIndexOf(':');
856: if (i != -1)
857: name = name.substring(i + 1);
858: else
859: name = null;
860: return name;
861: }
862:
863: /**
864: * Return true if this is the name of an attribute
865: */
866: public boolean isAttribute(String name) {
867: int i = name.lastIndexOf(':');
868: return (i != -1);
869: }
870:
871: /**
872: * Return the index value of the property, as a string
873: */
874: private static String extractPropertyIndex(String name) {
875: int i = name.lastIndexOf('/');
876: if (i != -1)
877: name = name.substring(i + 1);
878: i = name.lastIndexOf('.');
879: if (i != -1) {
880: name = name.substring(i + 1);
881: i = name.lastIndexOf(':');
882: if (i != -1)
883: name = name.substring(0, i);
884: } else
885: name = null;
886: return name;
887: }
888:
889: /**
890: * If the property is an indexed property, return the index of
891: * the property.
892: */
893: public int getPropertyIndex(String name) {
894: return getPropertyIndex(root, name);
895: }
896:
897: public static int getPropertyIndex(Bean theRoot, String name) {
898: String index = extractPropertyIndex(name);
899: if (index != null) {
900: int i = index.lastIndexOf('i');
901: if (i != -1) {
902: // This is a removed property - return the old value
903: return Integer.parseInt(index.substring(i + 1));
904: } else {
905: // Get the current index value
906: Bean bean = getPropertyParent(theRoot, name);
907: if (bean != null) {
908: BeanProp bp = bean.beanProp(getPropertyName(name));
909:
910: if (bp != null)
911: return bp
912: .idToIndex(Integer.parseInt(index, 16));
913: }
914: }
915: }
916:
917: return -1;
918: }
919:
920: //
921: // Events misc. methods
922: //
923: /////////////////////////////
924:
925: static public void debug(boolean d) {
926: DDLogFlags.debug = d;
927: }
928:
929: //
930: // Default values for scalar types. The idea is to allow the user to
931: // change the following default values (TODO).
932: //
933: public Object defaultScalarValue(int type) {
934: switch (type & Common.MASK_TYPE) {
935: case Common.TYPE_STRING:
936: return ""; // NOI18N
937: case Common.TYPE_BOOLEAN:
938: return Boolean.FALSE;
939: case Common.TYPE_BYTE:
940: return new Byte((byte) 0);
941: case Common.TYPE_CHAR:
942: return new Character('\0');
943: case Common.TYPE_SHORT:
944: return new Short((short) 0);
945: case Common.TYPE_INT:
946: return new Integer(0);
947: case Common.TYPE_LONG:
948: return new Long(0);
949: case Common.TYPE_FLOAT:
950: return new Float(0.0);
951: case Common.TYPE_DOUBLE:
952: return new Double(0.0);
953: default:
954: throw new IllegalArgumentException(Common.getMessage(
955: "UnknownType", type));
956: }
957: }
958: }
|