0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.core;
0043:
0044: import java.beans.PropertyChangeEvent;
0045: import java.beans.PropertyChangeListener;
0046: import java.beans.IntrospectionException;
0047: import java.io.IOException;
0048: import java.io.ObjectInputStream;
0049: import java.io.ObjectOutputStream;
0050: import java.util.ArrayList;
0051: import java.util.Arrays;
0052: import java.util.Collection;
0053: import java.util.Collections;
0054: import java.util.Enumeration;
0055: import java.util.HashMap;
0056: import java.util.HashSet;
0057: import java.util.Iterator;
0058: import java.util.LinkedList;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.Set;
0062: import java.util.logging.Level;
0063: import java.util.logging.Logger;
0064: import javax.swing.Action;
0065: import javax.swing.event.ChangeEvent;
0066: import javax.swing.event.ChangeListener;
0067: import org.netbeans.core.startup.ManifestSection;
0068: import org.openide.actions.MoveDownAction;
0069: import org.openide.actions.MoveUpAction;
0070: import org.openide.actions.PropertiesAction;
0071: import org.openide.actions.ReorderAction;
0072: import org.openide.actions.ToolsAction;
0073: import org.openide.filesystems.FileLock;
0074: import org.openide.filesystems.FileObject;
0075: import org.openide.filesystems.FileSystem;
0076: import org.openide.filesystems.MIMEResolver;
0077: import org.openide.filesystems.Repository;
0078: import org.openide.loaders.DataLoader;
0079: import org.openide.loaders.DataLoaderPool;
0080: import org.openide.loaders.InstanceSupport;
0081: import org.openide.modules.ModuleInfo;
0082: import org.openide.modules.SpecificationVersion;
0083: import org.openide.nodes.AbstractNode;
0084: import org.openide.nodes.BeanNode;
0085: import org.openide.nodes.Children;
0086: import org.openide.nodes.Node;
0087: import org.openide.util.HelpCtx;
0088: import org.openide.util.Lookup;
0089: import org.openide.util.LookupEvent;
0090: import org.openide.util.LookupListener;
0091: import org.openide.util.NbBundle;
0092: import org.openide.util.RequestProcessor;
0093: import org.openide.util.TopologicalSortException;
0094: import org.openide.util.Utilities;
0095: import org.openide.util.actions.SystemAction;
0096: import org.openide.util.io.NbMarshalledObject;
0097: import org.openide.util.io.NbObjectInputStream;
0098: import org.openide.util.io.NbObjectOutputStream;
0099:
0100: /** Node which represents loader pool and its content - all loaders
0101: * in the system. LoaderPoolNode also supports subnode reordering.<P>
0102: * LoaderPoolNode is singleton and that's why it can be obtained
0103: * only via call to static factory method getLoaderPoolNode().<P>
0104: * The same situation applies for NbLoaderPool inner class.
0105: * @author Dafe Simonek et al.
0106: */
0107: public final class LoaderPoolNode extends AbstractNode {
0108: /** The only instance of the LoaderPoolNode class in the system.
0109: * This value is returned from the getLoaderPoolNode() static method */
0110: private static LoaderPoolNode loaderPoolNode;
0111: private static final Logger err = Logger
0112: .getLogger("org.netbeans.core.LoaderPoolNode"); // NOI18N
0113:
0114: private static LoaderChildren myChildren;
0115:
0116: /** Array of DataLoader objects */
0117: private static List<DataLoader> loaders = new ArrayList<DataLoader>();
0118: /** Those which have been modified since being read from the pool */
0119: private static Set<DataLoader> modifiedLoaders = new HashSet<DataLoader>();
0120: /** Loaders by class name */
0121: private static Map<String, DataLoader> names2Loaders = new HashMap<String, DataLoader>(
0122: 200);
0123: /** Loaders by representation class name */
0124: private static Map<String, DataLoader> repNames2Loaders = new HashMap<String, DataLoader>(
0125: 200);
0126:
0127: /** Map from loader class names to arrays of class names for Install-Before's */
0128: private static Map<String, String[]> installBefores = new HashMap<String, String[]>();
0129: /** Map from loader class names to arrays of class names for Install-After's */
0130: private static Map<String, String[]> installAfters = new HashMap<String, String[]>();
0131:
0132: /** copy of the loaders to prevent copying */
0133: private static DataLoader[] loadersArray;
0134:
0135: /** true if changes in loaders should be notified */
0136: private static boolean installationFinished = false;
0137:
0138: /** if true, we are adding/removing a bunch of loaders; resort later */
0139: private static boolean updatingBatch = false;
0140: /** see above; true if at least one change */
0141: private static boolean updatingBatchUsed = false;
0142:
0143: /** Just workaround, need to pass instance of
0144: * the LoaderPoolNodeChildren as two params to superclass
0145: */
0146: private LoaderPoolNode() {
0147: super (new LoaderChildren());
0148:
0149: myChildren = (LoaderChildren) getChildren();
0150:
0151: setName("LoaderPoolNode"); // NOI18N
0152: setDisplayName(NbBundle.getMessage(LoaderPoolNode.class,
0153: "CTL_LoaderPool"));
0154:
0155: getCookieSet().add(new Index());
0156: getCookieSet().add(
0157: new InstanceSupport.Instance(getNbLoaderPool()));
0158: }
0159:
0160: public HelpCtx getHelpCtx() {
0161: return new HelpCtx(LoaderPoolNode.class);
0162: }
0163:
0164: /** Getter for set of actions that should be present in the
0165: * popup menu of this node.
0166: *
0167: * @return array of system actions that should be in popup menu
0168: */
0169: public Action[] getActions(boolean context) {
0170: return new Action[] { SystemAction.get(ReorderAction.class),
0171: null, SystemAction.get(ToolsAction.class),
0172: SystemAction.get(PropertiesAction.class), };
0173:
0174: }
0175:
0176: public static synchronized void beginUpdates() {
0177: updatingBatch = true;
0178: updatingBatchUsed = false;
0179: }
0180:
0181: public static synchronized void endUpdates() {
0182: if (!updatingBatch)
0183: throw new IllegalStateException();
0184: updatingBatch = false;
0185: if (updatingBatchUsed) {
0186: updatingBatchUsed = false;
0187: resort();
0188: }
0189: }
0190:
0191: /** Allows tests to wait while processing of events is finished.
0192: */
0193: public static void waitFinished() {
0194: getNbLoaderPool().fireTask.waitFinished();
0195: }
0196:
0197: /** Adds new loader when previous and following are specified.
0198: * An attempt will be made to (re-)order the loader pool according to specified
0199: * dependencies.
0200: * <p>If a loader of the same class already existed in the pool, that will be <b>removed</b>
0201: * and replaced with the new one.
0202: * @param s adds loader section
0203: */
0204: public static void add(ManifestSection.LoaderSection s)
0205: throws Exception {
0206: // the instantiation of the loader is done outside of synchronized block,
0207: // because foreign code is called and can cause deadlocks
0208: DataLoader l = (DataLoader) s.getInstance();
0209: doAdd(l, s);
0210: }
0211:
0212: /** Really adds the loader.
0213: */
0214: static synchronized void doAdd(DataLoader l,
0215: ManifestSection.LoaderSection s) throws Exception {
0216: if (err.isLoggable(Level.FINE) && s != null) {
0217: List before = s.getInstallBefore() == null ? null : Arrays
0218: .asList(s.getInstallBefore());
0219: List after = s.getInstallAfter() == null ? null : Arrays
0220: .asList(s.getInstallAfter());
0221: err.fine("add: " + l + " repclass: "
0222: + l.getRepresentationClass().getName()
0223: + " before: " + before + " after: " + after);
0224: }
0225: Iterator it = loaders.iterator();
0226: Class c = l.getClass();
0227: while (it.hasNext()) {
0228: if (it.next().getClass() == c) {
0229: it.remove();
0230: break;
0231: }
0232: }
0233: loaders.add(l);
0234: l.removePropertyChangeListener(getNbLoaderPool());
0235: l.addPropertyChangeListener(getNbLoaderPool());
0236:
0237: String cname = c.getName();
0238: names2Loaders.put(cname, l);
0239: repNames2Loaders.put(l.getRepresentationClassName(), l);
0240: if (s != null) {
0241: String[] ib = s.getInstallBefore();
0242: if (ib != null)
0243: installBefores.put(cname, ib);
0244: String[] ia = s.getInstallAfter();
0245: if (ia != null)
0246: installAfters.put(cname, ia);
0247: }
0248: if (updatingBatch) {
0249: updatingBatchUsed = true;
0250: } else {
0251: resort();
0252: }
0253: }
0254:
0255: /** Resort the loader pool according to stated dependencies.
0256: * Attempts to keep a stable order whenever possible, i.e. more-recently-installed
0257: * loaders will tend to stay near the end unless they need to be moved forward.
0258: * Note that dependencies on nonexistent (or unloadable) representation classes are simply
0259: * ignored and have no effect on ordering.
0260: * If there is a cycle (contradictory set of dependencies) in the loader pool,
0261: * its order is not changed.
0262: * In any case, a change event is fired afterwards.
0263: */
0264: private static synchronized void resort() {
0265: // A partial ordering over loaders based on their Install-* tags:
0266: Map<DataLoader, List<DataLoader>> deps = new HashMap<DataLoader, List<DataLoader>>();
0267: add2Deps(deps, installBefores, true);
0268: add2Deps(deps, installAfters, false);
0269: if (err.isLoggable(Level.FINE)) {
0270: err.fine("Before sort: " + loaders);
0271: }
0272:
0273: try {
0274: loaders = Utilities.topologicalSort(loaders, deps);
0275: if (err.isLoggable(Level.FINE)) {
0276: err.fine("After sort: " + loaders);
0277: }
0278: } catch (TopologicalSortException ex) {
0279: err.log(Level.WARNING, null, ex);
0280: err.warning("Contradictory loader ordering: " + deps); // NOI18N
0281: }
0282: update();
0283: }
0284:
0285: /**
0286: * Add to loader ordering dependencies.
0287: * Only pays attention to dependencies among loaders that actually exist.
0288: * @param deps a map from loaders to lists of loaders they must come before
0289: * @param orderings either {@link #installBefore} or {@link #installAfter}
0290: * @param before true if orderings refers to before, false if to after
0291: * @see Utilities#topologicalSort
0292: */
0293: private static void add2Deps(
0294: Map<DataLoader, List<DataLoader>> deps, Map orderings,
0295: boolean before) {
0296: Iterator it = orderings.entrySet().iterator();
0297: while (it.hasNext()) {
0298: Map.Entry e = (Map.Entry) it.next();
0299: String loaderClassName = (String) e.getKey();
0300: DataLoader l = names2Loaders.get(loaderClassName);
0301:
0302: if (l == null) {
0303: throw new IllegalStateException("No such loader: "
0304: + loaderClassName); // NOI18N
0305: }
0306: String[] repClassNames = (String[]) e.getValue();
0307:
0308: if (repClassNames == null) {
0309: throw new IllegalStateException("Null Install-"
0310: + (before ? "Before" : "After") + " for "
0311: + loaderClassName); // NOI18N
0312: }
0313: for (int i = 0; i < repClassNames.length; i++) {
0314: String repClassName = repClassNames[i];
0315: DataLoader l2 = repNames2Loaders.get(repClassName);
0316:
0317: if (l2 != null) {
0318: if (before) {
0319: addDep(deps, l, l2);
0320: } else {
0321: addDep(deps, l2, l);
0322: }
0323: } else {
0324: l2 = names2Loaders.get(repClassName);
0325: if (l2 != null) {
0326: warn(loaderClassName, repClassName, l2
0327: .getRepresentationClassName());
0328: }
0329: }
0330: }
0331: }
0332: }
0333:
0334: /**
0335: * Add one loader ordering dependency.
0336: * @param deps see {@link #add2Deps}
0337: * @param a the earlier loader
0338: * @param b the later loader
0339: */
0340: private static void addDep(Map<DataLoader, List<DataLoader>> deps,
0341: DataLoader a, DataLoader b) {
0342: List<DataLoader> l = deps.get(a);
0343: if (l == null) {
0344: deps.put(a, l = new LinkedList<DataLoader>());
0345: }
0346: if (!l.contains(b)) {
0347: l.add(b);
0348: }
0349: }
0350:
0351: /**
0352: * Warn about misuse of Install-{After,Before} to refer to loader class names rather
0353: * than representation class names.
0354: */
0355: private static void warn(String yourLoader, String otherLoader,
0356: String otherRepn) {
0357: err
0358: .warning("Warning: a possible error in the manifest containing "
0359: + yourLoader + " was found."); // NOI18N
0360: err
0361: .warning("The loader specified an Install-{After,Before} on "
0362: + otherLoader
0363: + ", but this is a DataLoader class."); // NOI18N
0364: err.warning("Probably you wanted " + otherRepn
0365: + " which is the loader's representation class."); // NOI18N
0366: }
0367:
0368: /** Notification to finish installation of nodes during startup.
0369: */
0370: static void installationFinished() {
0371: installationFinished = true;
0372:
0373: if (!modifiedLoaders.isEmpty()) {
0374: getNbLoaderPool().super FireChangeEvent();
0375: }
0376:
0377: if (myChildren != null) {
0378: myChildren.update();
0379: }
0380: }
0381:
0382: /** Checks whether a loader is modified. E.g. whether the loader
0383: * considers it to be modified and necessary to be saved.
0384: */
0385: static synchronized boolean isModified(DataLoader l) {
0386: return modifiedLoaders.contains(l);
0387: }
0388:
0389: /** Stores all the objects into stream.
0390: * @param oos object output stream to write to
0391: */
0392: private static synchronized void writePool(ObjectOutputStream oos)
0393: throws IOException {
0394: if (err.isLoggable(Level.FINE))
0395: err.fine("writePool");
0396: // No longer bother storing these (#29671):
0397: oos.writeObject(new HashMap()/*installBefores*/);
0398: oos.writeObject(new HashMap()/*installAfters*/);
0399:
0400: // Note which module each loader came from.
0401: Collection modules = Lookup.getDefault().lookupAll(
0402: ModuleInfo.class); // Collection<ModuleInfo>
0403:
0404: Iterator it = loaders.iterator();
0405:
0406: while (it.hasNext()) {
0407: DataLoader l = (DataLoader) it.next();
0408:
0409: if (!isModified(l)) {
0410: // #27190 - no real need to write this in detail.
0411: String c = l.getClass().getName();
0412: if (err.isLoggable(Level.FINE))
0413: err.fine("writing unmodified " + c);
0414: // '=' not a permissible part of a cnb, so this distinguishes it
0415: oos.writeObject("=" + c); // NOI18N
0416: continue;
0417: }
0418:
0419: NbMarshalledObject obj;
0420: try {
0421: obj = new NbMarshalledObject(l);
0422: } catch (IOException ex) {
0423: err.log(Level.WARNING, null, ex);
0424: obj = null;
0425: }
0426:
0427: if (obj != null) {
0428: if (err.isLoggable(Level.FINE))
0429: err.fine("writing modified "
0430: + l.getClass().getName());
0431: // Find its module, if any.
0432: Class c = l.getClass();
0433: Iterator mit = modules.iterator();
0434: boolean found = false;
0435: while (mit.hasNext()) {
0436: ModuleInfo m = (ModuleInfo) mit.next();
0437: if (m.isEnabled() && m.owns(c)) {
0438: if (err.isLoggable(Level.FINE))
0439: err.fine("belongs to module: "
0440: + m.getCodeNameBase());
0441: oos.writeObject(m.getCodeNameBase());
0442: int r = m.getCodeNameRelease();
0443: oos.writeInt(r); // might be -1, note
0444: SpecificationVersion v = m
0445: .getSpecificationVersion();
0446: if (v != null) {
0447: oos.writeObject(v.toString());
0448: } else {
0449: oos.writeObject(null);
0450: }
0451: found = true;
0452: break;
0453: }
0454: }
0455: if (!found) {
0456: if (err.isLoggable(Level.FINE))
0457: err.fine("does not belong to any module");
0458: // just write the NbMarshalledObject<DataLoader> itself;
0459: // we need to support that for compatibility of old loader
0460: // pools anyway
0461: }
0462: oos.writeObject(obj);
0463: }
0464: }
0465: if (err.isLoggable(Level.FINE))
0466: err.fine("writing null");
0467: oos.writeObject(null);
0468:
0469: // Write out system loaders now:
0470: Enumeration e = getNbLoaderPool().allLoaders();
0471: while (e.hasMoreElements()) {
0472: DataLoader l = (DataLoader) e.nextElement();
0473: if (loaders.contains(l))
0474: continue;
0475: if (!isModified(l)) {
0476: // #27190 again. No need to write anything
0477: String c = l.getClass().getName();
0478: if (err.isLoggable(Level.FINE))
0479: err.fine("skipping unmodified " + c);
0480: continue;
0481: }
0482: NbMarshalledObject obj;
0483: try {
0484: obj = new NbMarshalledObject(l);
0485: } catch (IOException ex) {
0486: err.log(Level.WARNING, null, ex);
0487: obj = null;
0488: }
0489: if (obj != null) {
0490: if (err.isLoggable(Level.FINE))
0491: err.fine("writing " + l.getClass().getName());
0492: // No associated module, no need to write such info.
0493: oos.writeObject(obj);
0494: }
0495: }
0496: if (err.isLoggable(Level.FINE))
0497: err.fine("writing null");
0498: oos.writeObject(null);
0499:
0500: if (err.isLoggable(Level.FINE))
0501: err.fine("done writing");
0502: }
0503:
0504: /** Reads loader from the input stream.
0505: * @param ois object input stream to read from
0506: */
0507: private static synchronized void readPool(ObjectInputStream ois)
0508: throws IOException, ClassNotFoundException {
0509: /*installBefores = (Map)*/ois.readObject();
0510: /*installAfters = (Map)*/ois.readObject();
0511:
0512: HashSet<Class> classes = new HashSet<Class>();
0513: LinkedList<DataLoader> l = new LinkedList<DataLoader>();
0514:
0515: Iterator<? extends ModuleInfo> mit = Lookup.getDefault()
0516: .lookupAll(ModuleInfo.class).iterator();
0517: Map<String, ModuleInfo> modules = new HashMap<String, ModuleInfo>();
0518: while (mit.hasNext()) {
0519: ModuleInfo m = mit.next();
0520: modules.put(m.getCodeNameBase(), m);
0521: }
0522:
0523: for (;;) {
0524: Object o1 = ois.readObject();
0525:
0526: if (o1 == null) {
0527: if (err.isLoggable(Level.FINE))
0528: err.fine("reading null");
0529: break;
0530: }
0531: NbMarshalledObject obj;
0532:
0533: if (o1 instanceof String) {
0534: String name = (String) o1;
0535: if (name.length() > 0 && name.charAt(0) == '=') { // NOI18N
0536: // #27190: unmodified loader, just here for the ordering.
0537: String cname = name.substring(1);
0538: DataLoader dl = names2Loaders.get(cname);
0539:
0540: if (dl != null) {
0541: if (err.isLoggable(Level.FINE))
0542: err.fine("reading unmodified " + cname);
0543: l.add(dl);
0544: classes.add(dl.getClass());
0545: } else {
0546: // No such known loaded - presumably disabled module.
0547: if (err.isLoggable(Level.FINE))
0548: err.fine("skipping unmodified nonexistent "
0549: + cname);
0550: }
0551: continue;
0552: }
0553: // Module information.
0554: int rel = ois.readInt();
0555: String spec = (String) ois.readObject();
0556:
0557: obj = (NbMarshalledObject) ois.readObject();
0558: ModuleInfo m = modules.get(name);
0559:
0560: if (m == null) {
0561: if (err.isLoggable(Level.FINE))
0562: err.fine("No known module " + name
0563: + ", skipping loader");
0564: continue;
0565: }
0566: if (!m.isEnabled()) {
0567: if (err.isLoggable(Level.FINE))
0568: err.fine("Module " + name
0569: + " is disabled, skipping loader");
0570: continue;
0571: }
0572: if (m.getCodeNameRelease() < rel) {
0573: if (err.isLoggable(Level.FINE))
0574: err
0575: .fine("Module "
0576: + name
0577: + " is too old (major vers.), skipping loader");
0578: continue;
0579: }
0580: if (spec != null) {
0581: SpecificationVersion v = m
0582: .getSpecificationVersion();
0583:
0584: if (v == null
0585: || v.compareTo(new SpecificationVersion(
0586: spec)) < 0) {
0587: if (err.isLoggable(Level.FINE))
0588: err
0589: .fine("Module "
0590: + name
0591: + " is too old (spec. vers.), skipping loader");
0592: continue;
0593: }
0594: }
0595: if (err.isLoggable(Level.FINE))
0596: err.fine("Module " + name
0597: + " is OK, will try to restore loader");
0598: } else {
0599: // Loader with no known module, or backward compatibility.
0600: obj = (NbMarshalledObject) o1;
0601: }
0602: Exception t = null;
0603:
0604: try {
0605: DataLoader loader = (DataLoader) obj.get();
0606:
0607: if (loader == null) {
0608: // loader that wishes to be skipped (right now WSLoader from
0609: // issue 38658)
0610: continue;
0611: }
0612: Class clazz = loader.getClass();
0613:
0614: if (err.isLoggable(Level.FINE))
0615: err.fine("reading modified " + clazz.getName());
0616: l.add(loader);
0617: classes.add(clazz);
0618: } catch (IOException ex) {
0619: t = ex;
0620: } catch (ClassNotFoundException ex) {
0621: t = ex;
0622: }
0623: }
0624:
0625: // Read system loaders. But not into any particular order.
0626: for (;;) {
0627: NbMarshalledObject obj = (NbMarshalledObject) ois
0628: .readObject();
0629: if (obj == null) {
0630: if (err.isLoggable(Level.FINE))
0631: err.fine("reading null");
0632: break;
0633: }
0634: Exception t = null;
0635: try {
0636: // Just reads its shared state, nothing more.
0637: DataLoader loader = (DataLoader) obj.get();
0638: if (err.isLoggable(Level.FINE))
0639: err.fine("reading " + loader.getClass().getName());
0640: } catch (IOException ex) {
0641: t = ex;
0642: } catch (ClassNotFoundException ex) {
0643: t = ex;
0644: }
0645: }
0646:
0647: if (err.isLoggable(Level.FINE))
0648: err.fine("done reading");
0649:
0650: // Explanation: modules are permitted to restoreDefault () before
0651: // the loader pool is de-externalized. This means that all loader manifest
0652: // sections will add a default-instance entry to the pool at startup
0653: // time. Later, when the pool is restored, this may reorder existing ones,
0654: // as well as change properties. But if any loader is missing (typically
0655: // due to failed deserialization), it will nonetheless be added to the end
0656: // now (and the pool resorted just in case).
0657:
0658: Iterator it = loaders.iterator();
0659: while (it.hasNext()) {
0660: DataLoader loader = (DataLoader) it.next();
0661: if (!classes.contains(loader.getClass())) {
0662: l.add(loader);
0663: }
0664: }
0665: if (l.size() > new HashSet<DataLoader>(l).size())
0666: throw new IllegalStateException("Duplicates in " + l); // NOI18N
0667:
0668: loaders = l;
0669: // Always "resort": if the existing order was in fact compatible with the
0670: // current install-befores/afters, then this is no op (besides firing an
0671: // update event). Cf. #29671.
0672: resort();
0673:
0674: }
0675:
0676: // I/O with loaders.ser; moved from NbProjectOperation:
0677: public static void store() throws IOException {
0678: FileObject ser = getLoaderPoolStorage(true);
0679: FileLock lock = ser.lock();
0680: try {
0681: ObjectOutputStream oos = new NbObjectOutputStream(ser
0682: .getOutputStream(lock));
0683: try {
0684: NbObjectOutputStream
0685: .writeSafely(oos, getNbLoaderPool());
0686: } finally {
0687: oos.close();
0688: }
0689: } finally {
0690: lock.releaseLock();
0691: }
0692: }
0693:
0694: public static void load() throws IOException {
0695: FileObject ser = getLoaderPoolStorage(false);
0696: if (ser != null) {
0697: ObjectInputStream ois = new NbObjectInputStream(ser
0698: .getInputStream());
0699: try {
0700: NbObjectInputStream.readSafely(ois);
0701: } finally {
0702: ois.close();
0703: }
0704: }
0705: }
0706:
0707: private static final String LOADER_POOL_NAME = "loaders.ser"; // NOI18N
0708:
0709: public static FileObject getLoaderPoolStorage(boolean create)
0710: throws IOException {
0711: FileSystem sfs = Repository.getDefault().getDefaultFileSystem();
0712: FileObject fo = sfs.findResource(LOADER_POOL_NAME);
0713: if (fo == null && create) {
0714: fo = sfs.getRoot().createData(LOADER_POOL_NAME);
0715: }
0716: return fo;
0717: }
0718:
0719: /** Notification that the state of pool has changed
0720: */
0721: private static synchronized void update() {
0722: if (err.isLoggable(Level.FINE))
0723: err.fine("update");
0724: // clear the cache of loaders
0725: loadersArray = null;
0726:
0727: NbLoaderPool lp = getNbLoaderPool();
0728: if (lp != null && installationFinished) {
0729: lp.super FireChangeEvent();
0730: }
0731:
0732: if (lp != null) {
0733: Enumeration e = lp.allLoaders();
0734: while (e.hasMoreElements()) {
0735: DataLoader l = (DataLoader) e.nextElement();
0736: // so the pool is there only once
0737: l.removePropertyChangeListener(lp);
0738: l.addPropertyChangeListener(lp);
0739: }
0740: }
0741: }
0742:
0743: /** Removes the loader. It is only removed from the list but
0744: * if an DataObject instance created exists it will be still
0745: * valid.
0746: * <P>
0747: * So the only difference is that when a DataObject is searched
0748: * for a FileObject this loader will not be taken into account.
0749: * <P>The loader pool may be resorted.
0750: * @param dl data loader to remove
0751: * @return true if the loader was registered and false if not
0752: */
0753: public static synchronized boolean remove(DataLoader dl) {
0754: if (loaders.remove(dl)) {
0755: if (err.isLoggable(Level.FINE))
0756: err.fine("remove: " + dl);
0757: String cname = dl.getClass().getName();
0758: names2Loaders.remove(cname);
0759: repNames2Loaders.remove(dl.getRepresentationClassName());
0760: installBefores.remove(cname);
0761: installAfters.remove(cname);
0762: dl.removePropertyChangeListener(getNbLoaderPool());
0763:
0764: if (updatingBatch) {
0765: updatingBatchUsed = true;
0766: } else {
0767: resort();
0768: }
0769: modifiedLoaders.remove(dl);
0770: return true;
0771: }
0772: return false;
0773: }
0774:
0775: /** Returns the only instance of the loader pool node in our system.
0776: * There's no other way to get an instance of this class,
0777: * loader pool node is singleton.
0778: * @return loader pool node instance
0779: */
0780: public static synchronized LoaderPoolNode getLoaderPoolNode() {
0781: if (loaderPoolNode == null)
0782: loaderPoolNode = new LoaderPoolNode();
0783: return loaderPoolNode;
0784: }
0785:
0786: /** Returns the only instance of the loader pool in our system.
0787: * There's no other way to get an instance of this class,
0788: * loader pool is singleton too.
0789: * @return loader pool instance
0790: */
0791: public static synchronized NbLoaderPool getNbLoaderPool() {
0792: if (nbLoaderPool == null) {
0793: nbLoaderPool = (NbLoaderPool) DataLoaderPool.getDefault();
0794: }
0795: return nbLoaderPool;
0796: }
0797:
0798: private static NbLoaderPool nbLoaderPool = null;
0799:
0800: /***** Inner classes **************/
0801:
0802: /** Node representing one loader in Loader Pool */
0803: private static class LoaderPoolItemNode extends
0804: BeanNode<DataLoader> {
0805:
0806: /** true if a system loader */
0807: boolean isSystem;
0808:
0809: /**
0810: * Constructs LoaderPoolItemNode for specified DataLoader.
0811: *
0812: * @param theBean bean for which we can construct BeanNode
0813: * @param parent The parent of this node.
0814: */
0815: public LoaderPoolItemNode(DataLoader loader)
0816: throws IntrospectionException {
0817: super (loader);
0818: setSynchronizeName(false);
0819: String displayName = getDisplayName();
0820: setName(loader.getClass().getName());
0821: isSystem = !loaders.contains(loader);
0822: if (isSystem) {
0823: setDisplayName(NbBundle.getMessage(
0824: LoaderPoolNode.class, "LBL_system_data_loader",
0825: displayName));
0826: } else {
0827: setDisplayName(displayName);
0828: }
0829: }
0830:
0831: /** Getter for set of actions that should be present in the
0832: * popup menu of this node.
0833: *
0834: * @return array of system actions that should be in popup menu
0835: */
0836: public Action[] getActions(boolean context) {
0837: if (isSystem)
0838: return new Action[] {
0839: SystemAction.get(ToolsAction.class),
0840: SystemAction.get(PropertiesAction.class), };
0841: else
0842: return new Action[] {
0843: SystemAction.get(MoveUpAction.class),
0844: SystemAction.get(MoveDownAction.class), null,
0845: SystemAction.get(ToolsAction.class),
0846: SystemAction.get(PropertiesAction.class), };
0847: }
0848:
0849: /** @return true
0850: */
0851: public Action getPreferredAction() {
0852: return SystemAction.get(PropertiesAction.class);
0853: }
0854:
0855: /** Cannot be deleted.
0856: * Any deleted loaders would reappear after refresh anyway.
0857: */
0858: public boolean canDestroy() {
0859: return false;
0860: }
0861:
0862: /** Cannot be copied
0863: */
0864: public boolean canCopy() {
0865: return false;
0866: }
0867:
0868: /** Cannot be cut
0869: */
0870: public boolean canCut() {
0871: return false;
0872: }
0873:
0874: public boolean canRename() {
0875: return false;
0876: }
0877:
0878: public HelpCtx getHelpCtx() {
0879: HelpCtx help = super .getHelpCtx();
0880: if (help == null
0881: || help.getHelpID() == null
0882: || help.getHelpID()
0883: .equals(BeanNode.class.getName())) {
0884: help = new HelpCtx(LoaderPoolItemNode.class);
0885: }
0886: return help;
0887: }
0888: } // end of LoaderPoolItemNode
0889:
0890: /** Implementation of children for LoaderPool node in explorer.
0891: * Extends Index.MapChildren implementation to map nodes to loaders and to support
0892: * children reordering.
0893: */
0894: private static final class LoaderChildren extends
0895: Children.Keys<DataLoader> implements ChangeListener {
0896: public LoaderChildren() {
0897: update();
0898: getNbLoaderPool().addChangeListener(this );
0899: }
0900:
0901: /** Update the the nodes */
0902: public void update() {
0903: setKeys(Collections.list(getNbLoaderPool().allLoaders()));
0904: }
0905:
0906: /** Creates new node for the loader.
0907: */
0908: protected Node[] createNodes(DataLoader loader) {
0909: try {
0910: return new Node[] { new LoaderPoolItemNode(loader) };
0911: } catch (IntrospectionException e) {
0912: err.log(Level.WARNING, null, e);
0913: return new Node[] {};
0914: }
0915: }
0916:
0917: public void stateChanged(ChangeEvent e) {
0918: update();
0919: }
0920:
0921: } // end of LoaderPoolChildren
0922:
0923: /** Concrete implementation of and abstract DataLoaderPool
0924: * (former CoronaLoaderPool).
0925: * Being a singleton, this class is private and the only system instance
0926: * can be obtained via LoaderPoolNode.getNbLoaderPool() call.
0927: * Delegates its work to the outer class LoaderPoolNode.
0928: */
0929: public static final class NbLoaderPool extends DataLoaderPool
0930: implements PropertyChangeListener, Runnable, LookupListener {
0931: private static final long serialVersionUID = -8488524097175567566L;
0932:
0933: private transient RequestProcessor.Task fireTask;
0934:
0935: private transient Lookup.Result mimeResolvers;
0936: private static RequestProcessor rp = new RequestProcessor(
0937: "Refresh Loader Pool"); // NOI18N
0938:
0939: public NbLoaderPool() {
0940: fireTask = rp.create(this , true);
0941: mimeResolvers = Lookup.getDefault().lookupResult(
0942: MIMEResolver.class);
0943: mimeResolvers.addLookupListener(this );
0944: }
0945:
0946: /** Enumerates all loaders. Loaders are taken from children
0947: * structure of LoaderPoolNode. */
0948: protected Enumeration<DataLoader> loaders() {
0949:
0950: //
0951: // prevents from extensive copying
0952: //
0953:
0954: DataLoader[] arr = loadersArray;
0955: if (arr == null) {
0956: synchronized (LoaderPoolNode.class) {
0957: arr = loadersArray = loaders
0958: .toArray(new DataLoader[loaders.size()]);
0959: }
0960: }
0961: return org.openide.util.Enumerations.array(arr);
0962: }
0963:
0964: /** Listener to property changes.
0965: */
0966: public void propertyChange(PropertyChangeEvent ev) {
0967: DataLoader l = (DataLoader) ev.getSource();
0968: String prop = ev.getPropertyName();
0969: if (DataLoader.PROP_ACTIONS.equals(prop)
0970: && ev.getNewValue() == null) {
0971: // skip this change as this means the loader is using new storage mechanism
0972: return;
0973: }
0974: modifiedLoaders.add(l);
0975: if (err.isLoggable(Level.FINE))
0976: err.fine("Got change in " + l.getClass().getName()
0977: + "." + prop);
0978: if (DataLoader.PROP_ACTIONS.equals(prop)
0979: || DataLoader.PROP_DISPLAY_NAME.equals(prop))
0980: return; // these are not important to the pool, i.e. to file recognition
0981: if (installationFinished) {
0982: super FireChangeEvent();
0983: }
0984: }
0985:
0986: /** Fires change event to all listeners
0987: * (Delegates all work to its superclass)
0988: * Accessor for inner classes only.
0989: * @param che change event
0990: */
0991: void super FireChangeEvent() {
0992: err.fine("Change in loader pool scheduled"); // NOI18N
0993: fireTask.schedule(1000);
0994: }
0995:
0996: /** Called from the request task */
0997: public void run() {
0998: err.fine("going to fire change in loaders"); // NOI18N
0999: super .fireChangeEvent(new ChangeEvent(this ));
1000: err.fine("change event fired"); // NOI18N
1001: }
1002:
1003: /** Write the object.
1004: */
1005: private void writeObject(ObjectOutputStream oos)
1006: throws IOException {
1007: LoaderPoolNode.writePool(oos);
1008: }
1009:
1010: /** Reads the object.
1011: */
1012: private void readObject(ObjectInputStream ois)
1013: throws IOException, ClassNotFoundException {
1014: LoaderPoolNode.readPool(ois);
1015: }
1016:
1017: /** Replaces the pool with default instance.
1018: */
1019: private Object readResolve() {
1020: return getNbLoaderPool();
1021: }
1022:
1023: public void resultChanged(LookupEvent ev) {
1024: if (org.netbeans.core.startup.Main.isInitialized()) {
1025: super FireChangeEvent();
1026: }
1027: }
1028: } // end of NbLoaderPool
1029:
1030: /** Index support for reordering of file system pool.
1031: */
1032: private final class Index extends org.openide.nodes.Index.Support {
1033:
1034: Index() {
1035: }
1036:
1037: /** Get the nodes; should be overridden if needed.
1038: * @return the nodes
1039: * @throws NotImplementedException always
1040: */
1041: public Node[] getNodes() {
1042: Enumeration e = getChildren().nodes();
1043: List<Node> l = new ArrayList<Node>();
1044: while (e.hasMoreElements()) {
1045: LoaderPoolItemNode node = (LoaderPoolItemNode) e
1046: .nextElement();
1047: if (!node.isSystem)
1048: l.add(node);
1049: }
1050: return l.toArray(new Node[l.size()]);
1051: }
1052:
1053: /** Get the node count. Subclasses must provide this.
1054: * @return the count
1055: */
1056: public int getNodesCount() {
1057: return getNodes().length;
1058: }
1059:
1060: /** Reorder by permutation. Subclasses must provide this.
1061: * @param perm the permutation
1062: */
1063: public void reorder(int[] perm) {
1064: synchronized (LoaderPoolNode.class) {
1065: DataLoader[] arr = loaders
1066: .toArray(new DataLoader[loaders.size()]);
1067:
1068: if (arr.length == perm.length) {
1069: DataLoader[] target = new DataLoader[arr.length];
1070: for (int i = 0; i < arr.length; i++) {
1071: if (target[perm[i]] != null) {
1072: throw new IllegalArgumentException();
1073: }
1074: target[perm[i]] = arr[i];
1075: }
1076:
1077: loaders = new ArrayList<DataLoader>(Arrays
1078: .asList(target));
1079: update();
1080: } else {
1081: throw new IllegalArgumentException();
1082: }
1083: }
1084: }
1085:
1086: } // End of Index
1087:
1088: }
|