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: package org.netbeans.modules.vmd.api.model;
042:
043: import org.netbeans.modules.vmd.model.XMLComponentDescriptor;
044: import org.netbeans.modules.vmd.model.XMLComponentProducer;
045: import org.openide.ErrorManager;
046: import org.openide.cookies.InstanceCookie;
047: import org.openide.filesystems.*;
048: import org.openide.loaders.DataFolder;
049: import org.openide.loaders.DataObject;
050: import org.openide.loaders.XMLDataObject;
051: import org.openide.util.Mutex;
052: import org.w3c.dom.Document;
053: import org.xml.sax.SAXException;
054: import java.io.IOException;
055: import java.lang.ref.WeakReference;
056: import java.util.*;
057: import java.util.HashMap;
058: import java.util.concurrent.CopyOnWriteArraySet;
059: import org.openide.filesystems.FileSystem.AtomicAction;
060: import org.openide.util.RequestProcessor;
061:
062: /**
063: * @author David Kaspar
064: */
065: // TODO - unique producers by their ids
066: final class GlobalDescriptorRegistry {
067:
068: // HINT - use WeakReference, then use FileChangeListener as WeakReference
069:
070: private static final HashMap<String, WeakReference<GlobalDescriptorRegistry>> registries = new HashMap<String, WeakReference<GlobalDescriptorRegistry>>();
071:
072: static GlobalDescriptorRegistry getGlobalDescriptorRegistry(
073: String projectType) {
074: assert Debug.isFriend(DescriptorRegistry.class,
075: "getDescriptorRegistry")
076: || Debug.isFriend(ComponentSerializationSupport.class); // NOI18N
077: synchronized (registries) {
078: WeakReference<GlobalDescriptorRegistry> ref = registries
079: .get(projectType);
080: GlobalDescriptorRegistry registry = ref != null ? ref.get()
081: : null;
082: if (registry == null) {
083: registry = new GlobalDescriptorRegistry(projectType);
084: registries.put(projectType,
085: new WeakReference<GlobalDescriptorRegistry>(
086: registry));
087: }
088: return registry;
089: }
090: }
091:
092: private final String projectType;
093:
094: private final DataFolder registryFolder;
095: private final DataFolder producersFolder;
096: private final Mutex mutex = new Mutex();
097:
098: private HashMap<TypeID, ComponentDescriptor> descriptors = new HashMap<TypeID, ComponentDescriptor>();
099: private ArrayList<ComponentProducer> producers = new ArrayList<ComponentProducer>();
100: private Map<TypeID, WeakReference<FileObject>> customFileObjects = new HashMap<TypeID, WeakReference<FileObject>>();
101: private Map<TypeID, WeakReference<FileObject>> customProducerFileObjects = new HashMap<TypeID, WeakReference<FileObject>>();
102: private final CopyOnWriteArraySet<DescriptorRegistryListener> listeners = new CopyOnWriteArraySet<DescriptorRegistryListener>();
103:
104: private final HashMap<String, WeakReference<DescriptorRegistry>> projectID2projectRegistry = new HashMap<String, WeakReference<DescriptorRegistry>>();
105:
106: private GlobalDescriptorRegistry(String projectType) {
107: assert projectType != null && projectType.length() > 0 : "Invalid project-type: "
108: + projectType; // NOI18N
109: this .projectType = projectType;
110:
111: FileObject registryFileObject = Repository.getDefault()
112: .getDefaultFileSystem().findResource(
113: projectType + "/components"); // NOI18N
114: if (registryFileObject != null) {
115: registryFolder = DataFolder.findFolder(registryFileObject);
116: registryFolder.getPrimaryFile().addFileChangeListener(
117: new FileChangeListener() {
118: public void fileFolderCreated(
119: FileEvent fileEvent) {
120: }
121:
122: public void fileDataCreated(FileEvent fileEvent) {
123: reloadLater();
124: }
125:
126: public void fileChanged(FileEvent fileEvent) {
127: reloadLater();
128: }
129:
130: public void fileDeleted(FileEvent fileEvent) {
131: reloadLater();
132: }
133:
134: public void fileRenamed(
135: FileRenameEvent fileRenameEvent) {
136: reloadLater();
137: }
138:
139: public void fileAttributeChanged(
140: FileAttributeEvent fileAttributeEvent) {
141: reloadLater();
142: }
143: });
144: } else
145: registryFolder = null;
146:
147: FileObject producersFileObject = Repository.getDefault()
148: .getDefaultFileSystem().findResource(
149: projectType + "/producers"); // NOI18N
150: if (producersFileObject != null) {
151: producersFolder = DataFolder
152: .findFolder(producersFileObject);
153: producersFolder.getPrimaryFile().addFileChangeListener(
154: new FileChangeListener() {
155: public void fileFolderCreated(
156: FileEvent fileEvent) {
157: }
158:
159: public void fileDataCreated(FileEvent fileEvent) {
160: reloadLater();
161: }
162:
163: public void fileChanged(FileEvent fileEvent) {
164: reloadLater();
165: }
166:
167: public void fileDeleted(FileEvent fileEvent) {
168: reloadLater();
169: }
170:
171: public void fileRenamed(
172: FileRenameEvent fileRenameEvent) {
173: reloadLater();
174: }
175:
176: public void fileAttributeChanged(
177: FileAttributeEvent fileAttributeEvent) {
178: reloadLater();
179: }
180: });
181: } else
182: producersFolder = null;
183: reload();
184: }
185:
186: DataFolder getRegistryFolder() {
187: assert Debug.isFriend(ComponentSerializationSupport.class,
188: "serializeComponentDescriptor")
189: || Debug.isFriend(ComponentSerializationSupport.class,
190: "refreshDescriptorRegistry"); // NOI18N
191: return registryFolder;
192: }
193:
194: DataFolder getProducersFolder() {
195: assert Debug.isFriend(ComponentSerializationSupport.class,
196: "serializeComponentProducer")
197: || Debug.isFriend(ComponentSerializationSupport.class,
198: "refreshDescriptorRegistry"); // NOI18N
199: return producersFolder;
200: }
201:
202: DescriptorRegistry getProjectRegistry(String projectID) {
203: assert Debug.isFriend(DescriptorRegistry.class,
204: "getDescriptorRegistry"); // NOI18N
205:
206: synchronized (projectID2projectRegistry) {
207: WeakReference<DescriptorRegistry> ref = projectID2projectRegistry
208: .get(projectID);
209: DescriptorRegistry registry = ref != null ? ref.get()
210: : null;
211: if (registry == null) {
212: registry = new DescriptorRegistry(this ); // projectType, projectID);
213: projectID2projectRegistry
214: .put(projectID,
215: new WeakReference<DescriptorRegistry>(
216: registry));
217: }
218: return registry;
219: }
220: }
221:
222: void readAccess(Runnable runnable) {
223: assert Debug.isFriend(DescriptorRegistry.class)
224: || Debug.isFriend(ComponentSerializationSupport.class,
225: "runUnderDescriptorRegistryReadAccess"); // NOI18N
226: mutex.readAccess(runnable);
227: }
228:
229: void writeAccess(final Runnable runnable) {
230: assert Debug.isFriend(DescriptorRegistry.class,
231: "removeComponentDescriptor")
232: || Debug.isFriend(ComponentSerializationSupport.class,
233: "runUnderDescriptorRegistryWriteAccess"); // NOI18N
234: mutex.writeAccess(runnable);
235: }
236:
237: private void reloadLater() {
238: RequestProcessor.getDefault().post(new Runnable() {
239: public void run() {
240: reload();
241: }
242: });
243: }
244:
245: void reload() {
246: // TODO - method call assertion
247: mutex.writeAccess(new Runnable() {
248: public void run() {
249: reloadCore();
250: }
251: });
252: }
253:
254: private void reloadCore() {
255: ArrayList<ComponentDescriptor> descriptorsList = new ArrayList<ComponentDescriptor>();
256: HashMap<TypeID, ComponentDescriptor> tempDescriptors = new HashMap<TypeID, ComponentDescriptor>();
257: ArrayList<ComponentProducer> tempProducers = new ArrayList<ComponentProducer>();
258: Map<TypeID, FileObject> tempFileObjects = new HashMap<TypeID, FileObject>();
259: Map<TypeID, FileObject> tempProducerFileObjects = new HashMap<TypeID, FileObject>();
260:
261: if (registryFolder != null) {
262: Enumeration<DataObject> enumeration = registryFolder
263: .children();
264:
265: while (enumeration.hasMoreElements()) {
266: DataObject dataObject = enumeration.nextElement();
267: ComponentDescriptor descriptor = dao2descriptor(
268: dataObject, tempFileObjects);
269:
270: if (descriptor == null) {
271: Debug.warning("No descriptor", dataObject
272: .getPrimaryFile().getNameExt()); // NOI18N
273: continue;
274: }
275: TypeDescriptor typeDescriptor = descriptor
276: .getTypeDescriptor();
277: if (typeDescriptor == null) {
278: Debug.warning("Null type descriptor", descriptor); // NOI18N
279: continue;
280: }
281:
282: final TypeID this Type = typeDescriptor.getThisType();
283: if (tempDescriptors.containsKey(this Type)) {
284: Debug.warning("Duplicate descriptor", this Type); // NOI18N
285: continue;
286: }
287:
288: descriptor.setSuperComponentDescriptor(null);
289: descriptor.setPropertyDescriptors(null);
290: descriptorsList.add(descriptor);
291: tempDescriptors.put(this Type, descriptor);
292:
293: ComponentProducer producer = ComponentProducer
294: .createDefault(descriptor);
295: if (producer != null)
296: tempProducers.add(producer);
297: }
298: }
299:
300: for (ComponentDescriptor descriptor : tempDescriptors.values())
301: resolveDescriptor(projectType, tempDescriptors, descriptor);
302:
303: tempDescriptors = new HashMap<TypeID, ComponentDescriptor>();
304: for (ComponentDescriptor descriptor : descriptorsList) {
305: TypeDescriptor typeDescriptor = descriptor
306: .getTypeDescriptor();
307: if (typeDescriptor.getSuperType() != null
308: && descriptor.getSuperDescriptor() == null) {
309: Debug
310: .warning("Unresolved super descriptor",
311: descriptor); // NOI18N
312: continue;
313: }
314: tempDescriptors.put(typeDescriptor.getThisType(),
315: descriptor);
316: }
317:
318: if (producersFolder != null) {
319: Enumeration<DataObject> enumeration = producersFolder
320: .children();
321: while (enumeration.hasMoreElements()) {
322: DataObject dataObject = enumeration.nextElement();
323: ComponentProducer producer = dao2producer(dataObject,
324: tempProducerFileObjects);
325: if (producer == null) {
326: Debug.warning("No producer", dataObject
327: .getPrimaryFile().getNameExt()); // NOI18N
328: continue;
329: }
330:
331: tempProducers.add(producer);
332: }
333: }
334:
335: Map<TypeID, WeakReference<FileObject>> tempCustomFileObjects = new HashMap<TypeID, WeakReference<FileObject>>();
336: for (TypeID key : tempFileObjects.keySet()) {
337: if (tempDescriptors.containsKey(key)) {
338: tempCustomFileObjects.put(key, new WeakReference(
339: tempFileObjects.get(key)));
340: }
341: }
342:
343: Map<TypeID, WeakReference<FileObject>> tempCustomProducerFileObjects = new HashMap<TypeID, WeakReference<FileObject>>();
344: for (TypeID key : tempProducerFileObjects.keySet()) {
345: if (tempDescriptors.containsKey(key)) {
346: tempCustomProducerFileObjects.put(key,
347: new WeakReference(tempProducerFileObjects
348: .get(key)));
349: }
350: }
351:
352: descriptors = tempDescriptors;
353: producers = tempProducers;
354: customFileObjects = tempCustomFileObjects;
355: customProducerFileObjects = tempCustomProducerFileObjects;
356:
357: for (DescriptorRegistryListener listener : listeners)
358: listener.descriptorRegistryUpdated();
359: }
360:
361: static void resolveDescriptor(String projectType,
362: HashMap<TypeID, ComponentDescriptor> allDescriptors,
363: ComponentDescriptor descriptor) {
364: resolveDescriptor(projectType, allDescriptors,
365: new HashSet<TypeID>(), descriptor);
366: }
367:
368: private static void resolveDescriptor(String projectType,
369: HashMap<TypeID, ComponentDescriptor> allDescriptors,
370: HashSet<TypeID> resolvingDescriptors,
371: ComponentDescriptor descriptor) {
372: assert Debug.isFriend(GlobalDescriptorRegistry.class)
373: || Debug.isFriend(DescriptorRegistry.class);
374:
375: TypeID this Type = descriptor.getTypeDescriptor().getThisType();
376: if (this Type == null) {
377: Debug.warning("Null TypeID", descriptor); // NOI18N
378: return;
379: }
380: if (resolvingDescriptors.contains(this Type)) {
381: Debug
382: .warning(
383: "There is inheritance-loop in CD registry - cannot resolve descriptor",
384: descriptor); // NOI18N
385: return;
386: }
387: resolvingDescriptors.add(this Type);
388: if (descriptor.getPropertyDescriptors() != null)
389: return;
390:
391: TypeID super Type = descriptor.getTypeDescriptor()
392: .getSuperType();
393: Collection<String> excludedNames = descriptor
394: .getExcludedPropertyDescriptorNames();
395: List<PropertyDescriptor> declaredDescriptors = descriptor
396: .getDeclaredPropertyDescriptors();
397: ArrayList<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
398: ComponentDescriptor super Descriptor = null;
399:
400: if (super Type != null) {
401: super Descriptor = allDescriptors.get(super Type);
402: if (super Descriptor == null) {
403: Debug.warning(
404: "Cannot find super descriptor for TypeID",
405: super Type); // NOI18N
406: return;
407: }
408: resolveDescriptor(projectType, allDescriptors,
409: super Descriptor);
410:
411: if (!super Descriptor.getTypeDescriptor().isCanDerive()) {
412: Debug.warning("Cannot derive from descriptor",
413: super Descriptor); // NOI18N
414: return;
415: }
416:
417: Collection<PropertyDescriptor> super PropertyDescriptors = super Descriptor
418: .getPropertyDescriptors();
419: if (super PropertyDescriptors == null) {
420: Debug
421: .warning(
422: "Missing super property descriptors - cannot resolve descriptor",
423: descriptor); // NOI18N
424: return;
425: }
426:
427: found: for (PropertyDescriptor propertyDescriptor : super PropertyDescriptors) {
428: String name = propertyDescriptor.getName();
429: if (name == null)
430: continue;
431: if (excludedNames != null)
432: for (String excludedName : excludedNames)
433: if (name.equals(excludedName))
434: continue found;
435: if (declaredDescriptors != null)
436: for (PropertyDescriptor declaredDescriptor : declaredDescriptors)
437: if (name.equals(declaredDescriptor.getName()))
438: continue found;
439: propertyDescriptors.add(propertyDescriptor);
440: }
441: }
442: if (declaredDescriptors != null) {
443: declared: for (int i = 0; i < declaredDescriptors.size(); i++) {
444: PropertyDescriptor declaredDescriptor = declaredDescriptors
445: .get(i);
446:
447: if (declaredDescriptor == null) {
448: Debug.warning("Null declared property descriptor",
449: descriptor); // NOI18N
450: continue;
451: }
452:
453: String name = declaredDescriptor.getName();
454: if (name == null) {
455: Debug.warning(
456: "Null declared property descriptor name",
457: name); // NOI18N
458: continue;
459: }
460:
461: for (int j = 0; j < i; j++) {
462: PropertyDescriptor tested = declaredDescriptors
463: .get(j);
464: if (tested != null && name.equals(tested.getName()))
465: continue declared;
466: }
467:
468: propertyDescriptors.add(declaredDescriptor);
469: }
470: }
471:
472: descriptor.setSuperComponentDescriptor(super Descriptor);
473:
474: descriptor.setPropertyDescriptors(null);
475: PropertiesProcessor.postProcessDescriptor(projectType,
476: descriptor, propertyDescriptors);
477: descriptor.setPropertyDescriptors(propertyDescriptors);
478: }
479:
480: private ComponentDescriptor dao2descriptor(DataObject dataObject,
481: Map<TypeID, FileObject> fileObjects) {
482: InstanceCookie.Of instanceCookie = dataObject
483: .getCookie(InstanceCookie.Of.class);
484: if (instanceCookie != null) {
485: try {
486: Object instance = instanceCookie.instanceCreate();
487: if (instance instanceof ComponentDescriptor)
488: return (ComponentDescriptor) instance;
489: } catch (IOException e) {
490: Debug.warning(e);
491: } catch (ClassNotFoundException e) {
492: Debug.warning(e);
493: }
494: Debug.warning("Instance is not ComponentDescriptor class"); // NOI18N
495: return null;
496: }
497:
498: if (dataObject instanceof XMLDataObject) {
499: ComponentDescriptor descriptor = deserializeComponentDescriptorFromXML((XMLDataObject) dataObject);
500: fileObjects.put(descriptor.getTypeDescriptor()
501: .getThisType(), dataObject.getPrimaryFile());
502: return descriptor;
503: }
504: return null;
505: }
506:
507: private ComponentProducer dao2producer(DataObject dataObject,
508: Map<TypeID, FileObject> fileObjects) {
509: InstanceCookie.Of instanceCookie = dataObject
510: .getCookie(InstanceCookie.Of.class);
511: if (instanceCookie != null) {
512: try {
513: Object instance = instanceCookie.instanceCreate();
514: if (instance instanceof ComponentProducer)
515: return (ComponentProducer) instance;
516: } catch (IOException e) {
517: Debug.warning(e);
518: } catch (ClassNotFoundException e) {
519: Debug.warning(e);
520: }
521: Debug.warning("Instance is not ComponentProducer class"); // NOI18N
522: return null;
523: }
524: if (dataObject instanceof XMLDataObject) {
525: return deserializeComponentCreatorFromXML(
526: (XMLDataObject) dataObject, fileObjects);
527: }
528: return null;
529: }
530:
531: private ComponentDescriptor deserializeComponentDescriptorFromXML(
532: XMLDataObject xmlDataObject) {
533: Document document;
534: try {
535: document = xmlDataObject.getDocument();
536: if (document == null)
537: return null;
538: } catch (IOException e) {
539: ErrorManager.getDefault().notify(e);
540: return null;
541: } catch (SAXException e) {
542: ErrorManager.getDefault().notify(e);
543: return null;
544: }
545: XMLComponentDescriptor descriptor = new XMLComponentDescriptor();
546: if (descriptor.deserialize(projectType, document))
547: return descriptor;
548: Debug.warning("Error during deserialization", xmlDataObject
549: .getPrimaryFile()); // NOI18N
550: return null;
551: }
552:
553: private ComponentProducer deserializeComponentCreatorFromXML(
554: XMLDataObject xmlDataObject,
555: Map<TypeID, FileObject> fileObjects) {
556: Document document;
557: try {
558: document = xmlDataObject.getDocument();
559: if (document == null)
560: return null;
561: } catch (IOException e) {
562: ErrorManager.getDefault().notify(e);
563: return null;
564: } catch (SAXException e) {
565: ErrorManager.getDefault().notify(e);
566: return null;
567: }
568: XMLComponentProducer producer = XMLComponentProducer
569: .deserialize(projectType, document);
570: if (producer != null) {
571: fileObjects.put(producer.getMainComponentTypeID(),
572: xmlDataObject.getPrimaryFile());
573: return producer;
574: }
575: Debug.warning("Error during deserialization", xmlDataObject
576: .getPrimaryFile()); // NOI18N
577: return null;
578: }
579:
580: Collection<ComponentDescriptor> getComponentDescriptors() {
581: return Collections.unmodifiableCollection(descriptors.values());
582: }
583:
584: List<ComponentProducer> getComponentProducers() {
585: return Collections.unmodifiableList(producers);
586: }
587:
588: void addRegistryListener(final DescriptorRegistryListener listener) {
589: listeners.add(listener);
590: }
591:
592: void removeRegistryListener(
593: final DescriptorRegistryListener listener) {
594: listeners.remove(listener);
595: }
596:
597: void removeComponentDescriptor(TypeID typeID) {
598: assert Debug.isFriend(
599: DescriptorRegistry.RemoveComponentDescriptorTask.class,
600: "run"); // NOI18N
601:
602: WeakReference<FileObject> weakReference = customFileObjects
603: .get(typeID);
604: final FileObject fo = weakReference != null ? weakReference
605: .get() : null;
606: if (fo != null) {
607: try {
608: Repository.getDefault().getDefaultFileSystem()
609: .runAtomicAction(new AtomicAction() {
610: public void run() {
611: try {
612: fo.delete();
613:
614: } catch (IOException ex) {
615: throw Debug.error(ex);
616: }
617: }
618: });
619: } catch (IOException ex) {
620: throw Debug.error(ex);
621: }
622: }
623:
624: weakReference = customProducerFileObjects.get(typeID);
625: final FileObject fo1 = weakReference != null ? weakReference
626: .get() : null;
627: if (fo1 != null) {
628: try {
629: Repository.getDefault().getDefaultFileSystem()
630: .runAtomicAction(new AtomicAction() {
631: public void run() {
632: try {
633: fo1.delete();
634:
635: } catch (IOException ex) {
636: throw Debug.error(ex);
637: }
638: }
639: });
640: } catch (IOException ex) {
641: throw Debug.error(ex);
642: }
643: }
644: }
645: }
|