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: /*
043:
044: How to use this class to display ConfigBeans next to your deployment
045: descriptor fragments:
046:
047: 1. Create the basebean
048: 2. Decide where you're going to potentially show sheets and customizers.
049: 3. Implement ModuleSupportCallback (minimal implementation fine for now).
050: 4. Create a ModuleDeploymentSupport instance.
051: 5. Get the standardDDBean back from the MDS.
052: 6. Query each of your plugins for their ConfigBeans.
053: 7. Give the ConfigBeans back to your MDS.
054: 8. When you want to show property sheets or components from the plugin,
055: call add{sheet,customizer}listener.
056: 9. Receive the {Sheets,Customizers} and display them/remove them when
057: pertinent
058: 10. When ModuleSupportCallback.beanModified() is called, activate the
059: save cookie on your DataObject.
060:
061: */
062: package org.netbeans.modules.j2ee.sun.share.config;
063:
064: import java.util.*;
065: import java.beans.*;
066:
067: import javax.enterprise.deploy.spi.*;
068: import javax.enterprise.deploy.spi.exceptions.*;
069: import javax.enterprise.deploy.model.*;
070: import javax.enterprise.deploy.shared.*;
071: import org.netbeans.modules.j2ee.dd.api.common.RootInterface;
072:
073: import org.openide.*;
074: import org.openide.nodes.*;
075: import org.openide.util.WeakListeners;
076:
077: import org.netbeans.modules.schema2beans.*;
078: import org.netbeans.modules.j2ee.deployment.plugins.api.*;
079: import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleProvider;
080: import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
081:
082: /**
083: */
084: public class ModuleDDSupport implements PropertyChangeListener {
085:
086: public static final String SEPARATOR = "/"; //NOI18N
087: public static final String WEBSERVICES_XML = "webservices.xml"; //NOI18N
088: private static Map filenameToPathMap = null;
089:
090: private Map rootMap = new HashMap(5); // DD location string -> DDRoot
091: private Map configMap = new IdentityHashMap(5); // DD object -> ConfigBeanStorage
092: private Map beanMap = Collections
093: .synchronizedMap(new IdentityHashMap()); // BaseBean -> StandardDDImpl
094: private Map leafMap = Collections
095: .synchronizedMap(new IdentityHashMap()); // BaseProp -> StandardDDImpl
096: private Map listenerMap = Collections
097: .synchronizedMap(new IdentityHashMap(5)); // BaseBean -> weak listener instance; //
098: private Set xpathListeners = new HashSet();
099: private J2eeModuleProvider provider;
100: private DeploymentConfiguration config;
101:
102: private static Map moduleDDlocationMap = new HashMap(10);
103:
104: static {
105: moduleDDlocationMap.put(J2eeModule.EAR,
106: new String[] { J2eeModule.APP_XML });
107: moduleDDlocationMap.put(J2eeModule.WAR, new String[] {
108: J2eeModule.WEB_XML, J2eeModule.WEBSERVICES_XML });
109: moduleDDlocationMap.put(J2eeModule.EJB, new String[] {
110: J2eeModule.EJBJAR_XML, J2eeModule.EJBSERVICES_XML });
111: moduleDDlocationMap.put(J2eeModule.CONN,
112: new String[] { J2eeModule.CONNECTOR_XML });
113: moduleDDlocationMap.put(J2eeModule.CLIENT,
114: new String[] { J2eeModule.CLIENT_XML });
115: }
116:
117: public static String[] getDDPaths(Object type) {
118: return (String[]) moduleDDlocationMap.get(type);
119: }
120:
121: public ModuleDDSupport(J2eeModuleProvider provider,
122: DeploymentConfiguration config) {
123: this .provider = provider;
124: this .config = config;
125: String[] ddLocs = getDDPaths(provider.getJ2eeModule()
126: .getModuleType());
127: for (int i = 0; i < ddLocs.length; i++) {
128: createRoot(ddLocs[i]);
129: }
130: }
131:
132: private ModuleType getModuleType() {
133: return (ModuleType) provider.getJ2eeModule().getModuleType();
134: }
135:
136: private RootInterface getDeploymentDescriptor(String ddLoc) {
137: // what do I do here?
138: RootInterface retVal = null;
139: //try {
140: // !PW deprecated (as will be this code very soon.
141: // retVal = provider.getJ2eeModule().getDeploymentDescriptor(ddLoc);
142: // } catch (Throwable t) {
143: // t.printStackTrace();
144: // }
145: return retVal;
146: }
147:
148: private DDRoot createRoot(String ddLoc) {
149: RootInterface bean = getDeploymentDescriptor(ddLoc);
150: if (bean == null) { // no support for that descriptor
151: return null;
152: }
153: // while(!bean.isRoot()) {
154: // bean = bean.parent();
155: // }
156: DDRoot root = new DDRoot(new DDNodeBean(null, bean, this ));
157: rootMap.put(ddLoc, root);
158: beanMap.put(bean, root);
159:
160: PropertyChangeListener weakListener = WeakListeners
161: .propertyChange(this , root.proxy.rooti);
162: listenerMap.put(bean, weakListener);
163: bean.addPropertyChangeListener(weakListener);
164:
165: return root;
166: }
167:
168: public DeployableObject getDeployableObject() {
169: return config.getDeployableObject();
170: }
171:
172: // This is broken in jsr88 that I even have to supply this
173: public DDRoot getDDBeanRoot() {
174: String loc = ((String[]) moduleDDlocationMap
175: .get(getModuleType()))[0];
176: return getDDBeanRoot(loc);
177: }
178:
179: public DDRoot getDDBeanRoot(String loc) {
180: DDRoot root = (DDRoot) rootMap.get(loc);
181:
182: // primary DD should be ready when this is called
183: // so this updating is only for non-primary DD's
184: if (root == null && !isPrimaryDD(loc, getType())) {
185: root = createRoot(loc);
186: DDRoot proot = getPrimaryDD();
187: ConfigBeanStorage configRoot = (ConfigBeanStorage) configMap
188: .get(proot);
189:
190: if (root != null && configRoot != null) {
191: DConfigBeanRoot cbroot = (DConfigBeanRoot) configRoot
192: .getConfigBean();
193: DConfigBean cb = cbroot.getDConfigBean(root);
194: if (cb != null) {
195: try {
196: ConfigBeanStorage cbs = new ConfigBeanStorage(
197: cb, null, configRoot.getStorage());
198: configMap.put(root, cbs);
199: } catch (Exception ex) {
200: ErrorManager.getDefault().notify(
201: ErrorManager.INFORMATIONAL, ex);
202: }
203: }
204: }
205: }
206: return root;
207: }
208:
209: public ModuleType getType() {
210: return getModuleType();
211: }
212:
213: public String getVersion() {
214: return provider.getJ2eeModule().getModuleVersion();
215: }
216:
217: /* PENDING get from CompilationUnit */
218: public Class getClassFromScope(String cls) {
219: return null; // provider.getClassFromScope(cls);
220: }
221:
222: public Node[] getNodes() {
223: String[] ddLocs = (String[]) moduleDDlocationMap
224: .get(getModuleType());
225: List ret = new ArrayList();
226: for (int i = 0; i < ddLocs.length; i++) {
227: Object dd = rootMap.get(ddLocs[i]);
228: if (dd == null) {
229: continue;
230: }
231: ConfigBeanStorage cbs = (ConfigBeanStorage) configMap
232: .get(dd);
233: if (cbs != null) {
234: Node n = cbs.getNode();
235: if (n != null) {
236: ret.add(n);
237: } else {
238: throw new RuntimeException(
239: "CBS.getNode returned null"); //NOI18N
240: }
241: }
242: }
243: return (Node[]) ret.toArray(new Node[ret.size()]);
244: }
245:
246: public void resetConfigCache() {
247: configMap = new IdentityHashMap(5); // DD object -> ConfigBeanStorage
248: xpathListeners = new HashSet();
249: }
250:
251: public void createConfigs(ConfigurationStorage storage)
252: throws ConfigurationException {
253: String[] ddLocs = (String[]) moduleDDlocationMap
254: .get(getModuleType());
255: DDRoot root = (DDRoot) rootMap.get(ddLocs[0]);
256: if (root != null) {
257: DConfigBeanRoot cbroot = config.getDConfigBeanRoot(root);
258: ConfigBeanStorage cbs = new ConfigBeanStorage(cbroot, null,
259: storage);
260: configMap.put(root, cbs);
261:
262: for (Iterator it = rootMap.keySet().iterator(); it
263: .hasNext();) {
264: String ddLoc = (String) it.next();
265: if (isPrimaryDD(ddLoc, getModuleType())) {
266: continue;
267: }
268: root = (DDRoot) rootMap.get(ddLoc);
269: if (root != null) {
270: DConfigBean cb = cbroot.getDConfigBean(root);
271: if (cb == null) {
272: continue;
273: }
274: ConfigBeanStorage cbStorage = new ConfigBeanStorage(
275: cb, null, storage);
276: configMap.put(root, cbStorage);
277: }
278: }
279: }
280: }
281:
282: static public boolean isPrimaryDD(String ddLocation, Object type) {
283: String[] ddLocs = (String[]) moduleDDlocationMap.get(type);
284: if (ddLocs.length < 1) {
285: return false;
286: }
287: return ddLocs[0].equals(ddLocation);
288: }
289:
290: static private Map filenameToPathMap() {
291: if (filenameToPathMap == null) {
292: filenameToPathMap = new HashMap();
293:
294: filenameToPathMap.put(filename(J2eeModule.APP_XML),
295: J2eeModule.APP_XML);
296: filenameToPathMap.put(filename(J2eeModule.WEB_XML),
297: J2eeModule.WEB_XML);
298: filenameToPathMap.put(filename(J2eeModule.EJBJAR_XML),
299: J2eeModule.EJBJAR_XML);
300: filenameToPathMap.put(filename(J2eeModule.CONNECTOR_XML),
301: J2eeModule.CONNECTOR_XML);
302: filenameToPathMap.put(filename(J2eeModule.CLIENT_XML),
303: J2eeModule.CLIENT_XML);
304:
305: filenameToPathMap.put(J2eeModule.APP_XML,
306: J2eeModule.APP_XML);
307: filenameToPathMap.put(J2eeModule.WEB_XML,
308: J2eeModule.WEB_XML);
309: filenameToPathMap.put(J2eeModule.EJBJAR_XML,
310: J2eeModule.EJBJAR_XML);
311: filenameToPathMap.put(J2eeModule.CONNECTOR_XML,
312: J2eeModule.CONNECTOR_XML);
313: filenameToPathMap.put(J2eeModule.CLIENT_XML,
314: J2eeModule.CLIENT_XML);
315: }
316: return filenameToPathMap;
317: }
318:
319: static private String filename(String path) {
320: int i = path.lastIndexOf(SEPARATOR);
321: return path.substring(i + 1);
322: }
323:
324: static public String filenameToPath(String filename, Object type) {
325: if (filename.endsWith(WEBSERVICES_XML)) {
326: if (J2eeModule.EJB.equals(type)) {
327: return J2eeModule.EJBSERVICES_XML;
328: } else {
329: return J2eeModule.WEBSERVICES_XML;
330: }
331: }
332: String name = (String) filenameToPathMap().get(filename);
333: if (name == null) {
334: name = filename;
335: }
336: return name;
337: }
338:
339: public DDRoot getPrimaryDD() {
340: ModuleType type = this .getType();
341: String[] ddLocs = (String[]) moduleDDlocationMap.get(type);
342: if (ddLocs.length < 1) {
343: return null;
344: }
345: return (DDRoot) rootMap.get(ddLocs[0]);
346: }
347:
348: /* Called when the module/app is closed from the ide, clean up listeners and
349: * references */
350: public void cleanup() {
351: // stop listening to DD changes
352: for (Iterator i = rootMap.values().iterator(); i.hasNext();) {
353: DDRoot root = (DDRoot) i.next();
354:
355: PropertyChangeListener weakListener = (PropertyChangeListener) listenerMap
356: .get(root.proxy.rooti);
357: root.proxy.rooti.removePropertyChangeListener(weakListener);
358:
359: // !PW Is this a good idea to add this here? What are the repercussions?
360: ConfigBeanStorage cbs = (ConfigBeanStorage) configMap
361: .get(root);
362: try {
363: if (config != null && cbs != null && cbs.bean != null) {
364: config
365: .removeDConfigBean((DConfigBeanRoot) cbs.bean);
366: }
367: } catch (BeanNotFoundException bnfe) {
368: // Log this, it shouldn't happen
369: ErrorManager.getDefault().log(
370: "BeanNotFoundException caught by ModuleDDSupport: "
371: + bnfe.getMessage());
372: }
373: }
374:
375: listenerMap = null;
376: rootMap = null;
377: configMap = null;
378: beanMap = null;
379: xpathListeners = null;
380: leafMap = null;
381: provider = null;
382: }
383:
384: /* Called when the module is removed from the app. */
385: // Not called from anywhere anymore, probably because multi-module support is
386: // disabled at the moment.
387: // public void dispose(DeploymentConfiguration config) {
388: // for(Iterator it = configMap.entrySet().iterator(); it.hasNext(); ) {
389: // Map.Entry entry = (Map.Entry) it.next();
390: // DDRoot root = (DDRoot) entry.getKey();
391: // ConfigBeanStorage cbs = (ConfigBeanStorage) entry.getValue();
392: // root.proxy.bean.removePropertyChangeListener(weakListener);
393: // try {
394: // config.removeDConfigBean((DConfigBeanRoot)cbs.bean);
395: // } catch (BeanNotFoundException bnfe) {
396: // // IGNORE
397: // }
398: // }
399: // }
400: StandardDDImpl getBean(BaseBean bean) {
401: // System.out.println("Getting bean for " + bean);
402: // System.out.println(bean.fullName());
403: // System.out.println(bean.dtdName());
404: if (bean == null) {
405: return null;
406: }
407:
408: if (beanMap == null) {
409: return null;
410: }
411:
412: StandardDDImpl ret = (StandardDDImpl) beanMap.get(bean);
413:
414: if (ret == null) {
415: /*
416: DDCommon base;
417: // System.out.println("Creating new bean");
418: BaseBean bb = bean;
419: while(!bb.isRoot()) {
420: bb = bb.parent();
421: if (bb== null) {
422: // We are in an unattached tree, we have expressed no prior
423: // interest in this Xpath so we just toss it.
424: // See: addTemporaryBean in this object
425: return null;
426: }
427:
428: }
429: if(bb == root.proxy.bean) base = new DDNodeBean(bean,this);
430: else { // must build proxy tree
431: if (bean.isRoot()) {
432: // PENDING This probably means that there is an error, can it legaly happen?
433: throw new IllegalStateException("Found a bean rooted in a tree not previously registered with Module Deployment Support. Bean = : " + bean + "@" + Integer.toHexString(bean.hashCode())); //NO I18N
434: }
435: StandardDDImpl parent = getBean(bean.parent());
436: base = new DDProxy(parent.proxy,bean,bean.dtdName(),this);
437: }
438: **/
439: if (!bean.isRoot()) {
440: ret = new StandardDDImpl(new DDNodeBean(bean, this ));
441: beanMap.put(bean, ret);
442: }
443: }
444: return ret;
445: }
446:
447: StandardDDImpl getBean(RootInterface bean) {
448: // System.out.println("Getting bean for " + bean);
449: // System.out.println(bean.fullName());
450: // System.out.println(bean.dtdName());
451: if (bean == null) {
452: return null;
453: }
454:
455: if (beanMap == null) {
456: return null;
457: }
458:
459: StandardDDImpl ret = (StandardDDImpl) beanMap.get(bean);
460:
461: if (ret == null) {
462: /*
463: DDCommon base;
464: // System.out.println("Creating new bean");
465: BaseBean bb = bean;
466: while(!bb.isRoot()) {
467: bb = bb.parent();
468: if (bb== null) {
469: // We are in an unattached tree, we have expressed no prior
470: // interest in this Xpath so we just toss it.
471: // See: addTemporaryBean in this object
472: return null;
473: }
474:
475: }
476: if(bb == root.proxy.bean) base = new DDNodeBean(bean,this);
477: else { // must build proxy tree
478: if (bean.isRoot()) {
479: // PENDING This probably means that there is an error, can it legaly happen?
480: throw new IllegalStateException("Found a bean rooted in a tree not previously registered with Module Deployment Support. Bean = : " + bean + "@" + Integer.toHexString(bean.hashCode())); //NO I18N
481: }
482: StandardDDImpl parent = getBean(bean.parent());
483: base = new DDProxy(parent.proxy,bean,bean.dtdName(),this);
484: }
485: **/
486: if (bean.getValue("parent") != null) {
487: ret = new StandardDDImpl(new DDNodeBean(bean, this ));
488: beanMap.put(bean, ret);
489: }
490: }
491: return ret;
492: }
493:
494: // for indexed leaf properties
495: StandardDDImpl getBean(BaseProperty prop, int index) {
496:
497: if (index < 0) {
498: return getBean(prop);
499: }
500:
501: if (!leafMap.containsKey(prop)) {
502: leafMap.put(prop, new StandardDDImpl[index + 1]);
503: } else if (((StandardDDImpl[]) leafMap.get(prop)).length <= index) {
504: StandardDDImpl[] a = (StandardDDImpl[]) leafMap.get(prop);
505: StandardDDImpl[] b = new StandardDDImpl[index + 1];
506:
507: leafMap.put(prop, b);
508: for (int i = 0; i < a.length; i++) {
509: b[i] = a[i];
510: }
511: }
512:
513: StandardDDImpl[] arr = (StandardDDImpl[]) leafMap.get(prop);
514: StandardDDImpl elem = arr[index];
515:
516: if (elem == null) {
517: elem = new StandardDDImpl(new DDLeafBean(prop, index, this ));
518: arr[index] = elem;
519: }
520:
521: return elem;
522: }
523:
524: // for non-indexed leaf properties
525: StandardDDImpl getBean(BaseProperty prop) {
526: StandardDDImpl elem = (StandardDDImpl) leafMap.get(prop);
527: if (elem == null) {
528: elem = new StandardDDImpl(new DDLeafBean(prop, this ));
529: leafMap.put(prop, elem);
530: }
531: return elem;
532: }
533:
534: StandardDDImpl getBean(String name) {
535: return getBean(name, getDDBeanRoot().proxy.rooti);
536: }
537:
538: StandardDDImpl getBean(String name, RootInterface rootBean) {
539: // FIXME
540: Bean parent = null; // GraphManager.getPropertyParent(rootBean, name);
541: //if (parent == null) {
542: return getDDBeanRoot();
543: //}
544: // String shortName = GraphManager.getPropertyName(name);
545: // int index = GraphManager.getPropertyIndex(rootBean, name);
546: // // System.out.println(name);
547: // // System.out.println(index);
548: //
549: // BaseProperty prop = parent.getProperty(shortName);
550: //
551: // if(index < 0 && prop.isIndexed()) {
552: // index = 0;
553: // }
554: //
555: // StandardDDImpl ret;
556: // if(prop.isBean()) {
557: // if(prop.isIndexed()) {
558: // ret = getBean((BaseBean) parent.getValue(shortName,index));
559: // } else {
560: // ret = getBean((BaseBean) parent.getValue(shortName));
561: // }
562: // }
563: // else {
564: // if(prop.isIndexed()) {
565: // ret = getBean(prop,index);
566: // } else {
567: // ret = getBean(prop);
568: // }
569: // }
570: // // System.out.println(ret.proxy.bean.fullName());
571: // // System.out.println(((Object)ret.proxy.bean).toString());
572: // return ret;
573: }
574:
575: void addXpathListener(DDCommon bean, String xpath,
576: XpathListener listen) {
577: xpathListeners
578: .add(new XpathListenerStorage(bean, xpath, listen));
579: }
580:
581: void removeXpathListener(DDCommon bean, String xpath,
582: XpathListener listen) {
583: xpathListeners.remove(new XpathListenerStorage(bean, xpath,
584: listen));
585: }
586:
587: /* functional spec for processing the PropertyChangeEvents:
588: *
589: * Ways in which listeners are added:
590: * 1. Customizer/Sheet Listeners
591: * 2. ConfigBean getChildBean()
592: * 3. ConfigBean associated Bean.
593: * 4. Xpath listeners (in all situations just fire XpathEvent)
594: *
595: * Types of Events:
596: * 1. Bean added
597: * 2. Bean removed
598: * 3. Bean changed
599: *
600: * 4. Plugin added
601: * 5. Plugin removed
602: * 6. Listener added
603: *
604: * Location of Event:
605: * 1. Current bean
606: * 2. Descendant bean
607: * 3. Ancestor bean (removal only)
608: *
609: * Other event type:
610: *
611: * Case-by-case breakdown:
612: *
613: * Location of Event: Current Bean
614: *
615: * Added Removed Changed
616: *
617: * Listener: N/A remove listener N/A
618: * getChild(): if matches,
619: * Call ConfigBean.getChild()
620: * Add it to parent property
621: * sheet if necessary.
622: * N/A N/A
623: * Bean: fire notifyStandardDDBean changed.
624: *
625: * Location of Event: Descendant Bean
626: *
627: * Added Removed Changed
628: *
629: * Listener: N/A
630: * getChild(): see above N/A N/A
631: * Bean: fire notifyStandardDDBean changed
632: *
633: * Location of Event: Ancestor Bean
634: *
635: * Added Removed Changed
636: *
637: * Listener: N/A
638: * getChild(): check match N/A N/A
639: * Bean: if removed is self,
640: * call removeChildBean()
641: * on parent
642: *
643: * Other events:
644: * ListenerAdded PluginAdded PluginRemoved
645: * Listener: N/A Calculate display for new remove display
646: * plugin
647: * getChild(): Calculate display Call all getChild() remove listeners
648: * for associated methods applicable
649: * ConfigBean
650: * bean: as above as above remove listeners
651: */
652:
653: public void propertyChange(PropertyChangeEvent event) {
654:
655: Object oldValue = event.getOldValue();
656: // System.out.println("Old value" + oldValue);
657: Object newValue = event.getNewValue();
658: // System.out.println("New value" + newValue);
659: String name = event.getPropertyName();
660:
661: // System.out.println("Processing ddbeans event " + name);
662: // System.out.println("From source " + event.getSource());
663: // System.out.println(event.getSource().getClass());
664: if (rootMap == null) {
665: ErrorManager
666: .getDefault()
667: .notify(
668: ErrorManager.INFORMATIONAL,
669: new Exception(
670: "ModuleDDSupport: Unexpected change event (NAME="
671: + name
672: + ", old="
673: + oldValue
674: + ", new="
675: + newValue
676: + ") received on previously removed DDBean->DConfigBean graph. See IZ 81332."));
677: // Do not process events if this support object has been destroyed.
678: return;
679: }
680:
681: try {
682: StandardDDImpl eventBean = null;
683: if (newValue == null && oldValue instanceof BaseBean) {
684: eventBean = getBean((BaseBean) oldValue);
685: } else {
686: Object eventObj = oldValue != null ? oldValue
687: : newValue;
688: if (!(eventObj instanceof BaseBean)) {
689: eventObj = event.getSource();
690: }
691: if (eventObj instanceof BaseBean) {
692: BaseBean root = (BaseBean) eventObj;
693: while (!root.isRoot()) {
694: root = root.parent();
695: }
696: //check if same root that we saw
697: boolean rootInCache = false;
698: for (Iterator ddRoots = rootMap.values().iterator(); ddRoots
699: .hasNext();) {
700: DDRoot ddroot = (DDRoot) ddRoots.next();
701: if (ddroot.proxy != null
702: && ddroot.proxy.rooti == root) {
703: rootInCache = true;
704: break;
705: }
706: }
707: if (rootInCache) {
708: //FIXME
709: //eventBean = getBean(name, root);
710: }
711: }
712: if (eventBean == null) {
713: eventBean = getBean(name);
714: }
715: }
716:
717: // this is the case where an array assignment is made
718: // too change a whole set of properties, and to make
719: // sense of the events. Our UI only generates these
720: // array assignments if the oldvalue is non-null.
721: if (eventBean == null && oldValue instanceof Object[]) {
722: // process separate propertyChange events for each
723: // array element.
724: List newElements = new ArrayList();
725: if (newValue != null) {
726: Object[] newValues = (Object[]) newValue;
727: for (int i = 0; i < newValues.length; i++) {
728: if (newValues[i] == null) {
729: continue;
730: }
731: newElements.add(newValues[i]);
732: }
733: }
734: Object[] values = (Object[]) oldValue;
735: for (int i = 0; i < values.length; i++) {
736: Object value = values[i];
737: // PENDING tracking indicies of non-BaseBean
738: // properties does not work.
739: if (!(value instanceof BaseBean)) {
740: break;
741: }
742: // no change in this element
743: if (newElements.contains(value)) {
744: newElements.remove(value);
745: continue;
746: }
747: StandardDDImpl valueBean = getBean((BaseBean) value);
748: // I still don't know anything about this bean.
749: if (valueBean == null) {
750: continue;
751: }
752: // this element has been removed.
753: processEvent(value, null, valueBean.proxy, event);
754: }
755: /*for(Iterator i = newElements.iterator();i.hasNext();) {
756: i.next();
757: // PENDING ignore for now - these should have already
758: // generated events for adds?
759: }*/
760: }
761:
762: // swallow events we know nothing about.
763: if (eventBean == null) {
764: return;
765: }
766:
767: if (oldValue == null && eventBean.proxy.isProxy()) {
768: eventBean.setProxy(new DDNodeBean(
769: (DDProxy) eventBean.proxy));
770: return; // swallow this event
771: }
772:
773: processEvent(oldValue, newValue, eventBean.proxy, event);
774:
775: } catch (Exception e) {
776: ErrorManager.getDefault().notify(e);
777: }
778: }
779:
780: void processEvent(Object oldValue, Object newValue,
781: DDCommon eventBean, PropertyChangeEvent event) {
782:
783: // System.out.println("Processing event on " + eventBean);
784:
785: // Start with just XpathEvents.
786: // 0. Make the StandardDDBean for the Event, get its Xpath.
787: // 1. Iterate through all the listeners. // optimize lookup later
788: // 2. for listener l
789: // 3. Make the listener's xpath.
790: // 4. If (3) is related to source, continue.
791: // 5. Find the real BB + xpath for the listener
792: // 6. Check the ancestry relationship between the Event's BB
793: // and the Listener's BB
794: // 7. If share ancestry, Construct XpathEvent, fire.
795:
796: // PENDING should get from source + property
797:
798: String eventDtdPath = eventBean.getXpath();
799:
800: Object type = XpathEvent.BEAN_CHANGED;
801: if (oldValue == null) {
802: type = XpathEvent.BEAN_ADDED;
803: }
804: if (newValue == null) {
805: type = XpathEvent.BEAN_REMOVED;
806: }
807: XpathEvent xe = new XpathEvent(eventBean.container, type);
808: xe.setChangeEvent(event);
809:
810: Object xpathListenerArray[] = xpathListeners.toArray();
811: for (int i = 0; i < xpathListenerArray.length; i++) {
812: XpathListenerStorage x = (XpathListenerStorage) xpathListenerArray[i];
813: if (x.bean == null) {
814: continue;
815: }
816: String xp = x.getNormalizedPath();
817:
818: // System.out.println("Checking against listener " + xp);
819:
820: //PENDING - handle delete events on completely different code path?
821: // need to get this code working for DDBean ancestry traversal.
822: DDCommon leftBean, rightBean;
823: if (eventDtdPath.startsWith(xp)) {
824: // System.out.println("Event dtd is smaller");
825: leftBean = x.bean;
826: rightBean = eventBean;
827: } else if (xp.startsWith(eventDtdPath)) {
828: // System.out.println("Event dtd is bigger");
829: leftBean = eventBean;
830: rightBean = x.bean;
831: } else {
832: continue;
833: }
834: while (leftBean != rightBean && rightBean != null) {
835: rightBean = rightBean.parent;
836: }
837: if (leftBean == rightBean) {
838: x.listen.fireXpathEvent(xe);
839: }
840: }
841: // should look through DDBeans we know about and check for
842: // relative listeners that way. This perhaps means we pop
843: // up the event bean ancestor list and just look up the DDBean
844: // directly to process events.
845: eventBean.fireEvent(xe);
846: // PENDING remove should remove the DDBean and any children from the cache.
847:
848: }
849:
850: private static class XpathListenerStorage {
851: private DDCommon bean = null;
852: private String xpath;
853: private boolean xpathRelative;
854: private XpathListener listen;
855: private String normal = null;
856:
857: XpathListenerStorage(DDCommon bean, String xpath,
858: XpathListener listen) {
859: this .bean = bean;
860: this .xpath = xpath;
861: this .listen = listen;
862: xpathRelative = !xpath.startsWith(SEPARATOR);
863: }
864:
865: public String getNormalizedPath() {
866: if (normal == null) {
867: String base = xpath;
868: if (xpathRelative) {
869: base = bean.getXpath() + SEPARATOR + base;
870: }
871: normal = normalizePath(base);
872: }
873: return normal;
874: }
875:
876: public String toString() {
877: return bean + " " + xpath + " " + listen;
878: }
879:
880: public int hashCode() {
881: return listen.hashCode();
882: }
883:
884: public boolean equals(Object o) {
885: if (o instanceof XpathListenerStorage) {
886: XpathListenerStorage x = (XpathListenerStorage) o;
887: return (x.bean == bean) && (x.xpath.equals(xpath))
888: && (x.listen == listen);
889: }
890: return false;
891: }
892:
893: }
894:
895: static String normalizePath(String path) {
896: boolean absolute = path.startsWith(SEPARATOR);
897: StringTokenizer tokens = new StringTokenizer(path, SEPARATOR,
898: false);
899:
900: LinkedList l = new LinkedList();
901:
902: while (tokens.hasMoreElements()) {
903: l.addLast(tokens.nextElement());
904: }
905:
906: for (int i = 0; i < l.size();) {
907: String tok = (String) l.get(i);
908: if (tok.equals(".")) {
909: l.remove(i);
910: } else if (tok.equals("..") && i > 0
911: && !l.get(i - 1).equals("..")) {
912: l.remove(i);
913: l.remove(i - 1);
914: i--;
915: } else {
916: i++;
917: }
918: }
919:
920: StringBuffer ret = new StringBuffer();
921:
922: for (int i = 0; i < l.size(); i++) {
923: if (absolute || i > 0) {
924: ret.append(SEPARATOR);
925: }
926: ret.append(l.get(i));
927: }
928:
929: return ret.toString();
930:
931: }
932:
933: public J2eeModuleProvider getProvider() {
934: return provider;
935: }
936:
937: RootInterface getRootInterface() {
938: return null;
939: }
940: }
|