Loaders are registered in layer files
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/manifest.mf
--- a/openide.loaders/manifest.mf Tue Jun 03 15:47:50 2008 -0700
+++ b/openide.loaders/manifest.mf Fri Jun 06 17:19:40 2008 +0200
@@ -1,6 +1,6 @@ Manifest-Version: 1.0
Manifest-Version: 1.0
OpenIDE-Module: org.openide.loaders
-OpenIDE-Module-Specification-Version: 7.0
+OpenIDE-Module-Specification-Version: 7.1
OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties
OpenIDE-Module-Recommends: org.netbeans.modules.templates.v1_0
AutoUpdate-Essential-Module: true
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/src/org/openide/loaders/DataLoaderPool.java
--- a/openide.loaders/src/org/openide/loaders/DataLoaderPool.java Tue Jun 03 15:47:50 2008 -0700
+++ b/openide.loaders/src/org/openide/loaders/DataLoaderPool.java Fri Jun 06 17:19:40 2008 +0200
@@ -41,7 +41,10 @@
package org.openide.loaders;
+import java.awt.Image;
import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -476,7 +479,12 @@ implements java.io.Serializable {
Enumeration extends DataObject.Factory> en = allLoaders (fo);
while (en.hasMoreElements ()) {
DataObject.Factory l = en.nextElement ();
- DataObject obj = l.findDataObject (fo, recognized);
+ DataObject obj;
+ if (l instanceof DataLoader) {
+ obj = l.findDataObject (fo, recognized);
+ } else {
+ obj = DataObjectPool.handleFindDataObject(l, fo, recognized);
+ }
if (!recognized.isEmpty()) {
for (FileObject f : recognized) {
r.markRecognized(f);
@@ -575,7 +583,42 @@ implements java.io.Serializable {
}
return null;
}
-
+
+ /** Factory method to create default implementation of a factory for
+ * data objects. It takes the class of the DataObject
and
+ * is ready to call its constructor. The constructor needs to take two
+ * arguments: FileObject and MultiDataLoader. It can throw IOException as
+ * is usual among DataObject constructors.
+ *
+ * You can also invoke this method from a layer by following definition:
+ *
+ * <file name="nameofyourfile.instance">
+ * <attr name="instanceCreate" methodvalue="org.openide.loaders.DataLoaderPool.factory"/>
+ * <attr name="dataObjectClass" stringvalue="org.your.pkg.YourDataObject"/>
+ * <attr name="mimeType" stringvalue="yourmime/type"/>
+ * <attr name="SystemFileSystem.localizingIcon" stringvalue="org/your/pkg/YourDataObject.png"/>
+ * </file>
+ *
+ * @param clazz the class of the data object to create. Must have appropriate
+ * constructor.
+ * @param mimeType the mime type associated with the object, used for
+ * example to create the right actions for the object's node
+ * @param image icon to use by default for nodes representing data objects
+ * created with this factory
+ * @return factory to be registered in Loaders/mime/type/Factories
+ * in some module layer file
+ * @since 7.1
+ */
+ public static DataObject.Factory factory(
+ Class dataObjectClass, String mimeType, Image image
+ ) {
+ return new MimeFactory(dataObjectClass, mimeType, image, null);
+ }
+ static DataObject.Factory factory(
+ FileObject fo
+ ) throws ClassNotFoundException {
+ return MimeFactory.layer(fo);
+ }
/** Lazy getter for system loaders.
*/
@@ -801,11 +844,7 @@ private static class InstanceLoader exte
};
}
} // end of InstanceLoader
-
-
-
-
/** Loader for file objects not recognized by any other loader */
private static final class DefaultLoader extends MultiFileLoader {
static final long serialVersionUID =-6761887227412396555L;
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/src/org/openide/loaders/DataNode.java
--- a/openide.loaders/src/org/openide/loaders/DataNode.java Tue Jun 03 15:47:50 2008 -0700
+++ b/openide.loaders/src/org/openide/loaders/DataNode.java Fri Jun 06 17:19:40 2008 +0200
@@ -246,6 +246,11 @@ public class DataNode extends AbstractNo
}
return super.getHtmlDisplayName();
}
+
+ private java.awt.Image getImageFromFactory(int type) {
+ MimeFactory> fact = getLookup().lookup(MimeFactory.class);
+ return fact != null ? fact.getImage(type) : null;
+ }
/** Get the displayed icon for this node.
* A filesystem may {@link org.openide.filesystems.FileSystem#getStatus specially alter} this.
@@ -254,8 +259,12 @@ public class DataNode extends AbstractNo
* @param type the icon type from {@link java.beans.BeanInfo}
* @return the desired icon
*/
+ @Override
public java.awt.Image getIcon (int type) {
- java.awt.Image img = super.getIcon (type);
+ java.awt.Image img = getImageFromFactory(type);
+ if (img == null) {
+ img = super.getIcon (type);
+ }
try {
img = obj.getPrimaryFile ().getFileSystem ().getStatus ().annotateIcon (img, type, new LazyFilesSet());
@@ -273,8 +282,12 @@ public class DataNode extends AbstractNo
* @param type the icon type from {@link java.beans.BeanInfo}
* @return the desired icon
*/
+ @Override
public java.awt.Image getOpenedIcon (int type) {
- java.awt.Image img = super.getOpenedIcon(type);
+ java.awt.Image img = getImageFromFactory(type);
+ if (img == null) {
+ img = super.getOpenedIcon(type);
+ }
try {
img = obj.getPrimaryFile ().getFileSystem ().getStatus ().annotateIcon (img, type, new LazyFilesSet());
@@ -351,6 +364,11 @@ public class DataNode extends AbstractNo
if (systemActions != null) {
return systemActions;
+ }
+
+ MimeFactory> mime = getLookup().lookup(MimeFactory.class);
+ if (mime != null) {
+ return mime.getActions();
}
return obj.getLoader ().getSwingActions ();
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/src/org/openide/loaders/DataObjectPool.java
--- a/openide.loaders/src/org/openide/loaders/DataObjectPool.java Tue Jun 03 15:47:50 2008 -0700
+++ b/openide.loaders/src/org/openide/loaders/DataObjectPool.java Fri Jun 06 17:19:40 2008 +0200
@@ -156,6 +156,26 @@ implements ChangeListener {
getPOOL ().enterRecognition(fo);
ret = loader.handleFindDataObject (fo, rec);
+ } finally {
+ exitAllowConstructor (prev);
+ }
+
+ return ret;
+ }
+
+ /** Calls into one loader. Setups security condition to allow DataObject ocnstructor
+ * to succeed.
+ */
+ public static DataObject handleFindDataObject (DataObject.Factory factory, FileObject fo, Set super FileObject> rec)
+ throws java.io.IOException {
+ DataObject ret;
+
+ Collection- prev = enterAllowContructor();
+ try {
+ // make sure this thread is allowed to recognize
+ getPOOL ().enterRecognition(fo);
+
+ ret = factory.findDataObject (fo, rec);
} finally {
exitAllowConstructor (prev);
}
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/src/org/openide/loaders/MimeFactory.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openide.loaders/src/org/openide/loaders/MimeFactory.java Fri Jun 06 17:19:40 2008 +0200
@@ -0,0 +1,156 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.openide.loaders;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.Set;
+import javax.swing.Action;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileStateInvalidException;
+import org.openide.filesystems.Repository;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.Utilities;
+
+/** Default DataObject.Factory implementation.
+ *
+ * @author Jaroslav Tulach
+ * @param type of DataObject to create
+ */
+class MimeFactory implements DataObject.Factory {
+ final Class extends T> clazz;
+ final Constructor extends T> factory;
+ final String mimeType;
+ Image img;
+ final FileObject fo;
+
+ public MimeFactory(Class extends T> clazz, String mimeType, Image img, FileObject fo) {
+ super();
+ this.clazz = clazz;
+ this.mimeType = mimeType;
+ this.img = img;
+ try {
+ this.factory = clazz.getConstructor(FileObject.class, MultiFileLoader.class);
+ this.factory.setAccessible(true);
+ } catch (NoSuchMethodException ex) {
+ throw (IllegalStateException) new IllegalStateException(ex.getMessage()).initCause(ex);
+ }
+ this.fo = fo;
+ }
+
+ public static MimeFactory layer(FileObject fo) throws ClassNotFoundException {
+ String className = (String) fo.getAttribute("dataObjectClass"); // NOI18N
+ if (className == null) {
+ throw new IllegalStateException("No attribute dataObjectClass for " + fo);
+ }
+ String mimeType = (String)fo.getAttribute("mimeType"); // NOI18N
+
+ ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
+ if (l == null) {
+ l = Thread.currentThread().getContextClassLoader();
+ }
+ if (l == null) {
+ l = MimeFactory.class.getClassLoader();
+ }
+ Class extends DataObject> clazz = l.loadClass(className).asSubclass(DataObject.class);
+ return new MimeFactory(clazz, mimeType, null, fo);
+ }
+
+ public DataObject findDataObject(FileObject fo, Set super FileObject> recognized) throws IOException {
+ DataObject obj = null;
+ Exception e = null;
+ try {
+ obj = factory.newInstance(fo, DataLoaderPool.getDefaultFileLoader());
+ } catch (InstantiationException ex) {
+ e = ex;
+ } catch (IllegalAccessException ex) {
+ e = ex;
+ } catch (IllegalArgumentException ex) {
+ e = ex;
+ } catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof IOException) {
+ throw (IOException)ex.getTargetException();
+ }
+ e = ex;
+ }
+ if (obj == null) {
+ throw (IOException) new IOException(e.getMessage()).initCause(e);
+ }
+ if (obj instanceof MultiDataObject) {
+ MultiDataObject mdo = (MultiDataObject) obj;
+ mdo.getCookieSet().assign(DataObject.Factory.class, this);
+ }
+ return obj;
+ }
+
+ final Image getImage(int type) {
+ if (img == null && fo != null) {
+ img = Utilities.loadImage("org/openide/loaders/empty.gif", true); // NOI18N
+ try {
+ img = fo.getFileSystem().getStatus().annotateIcon(img, type, Collections.singleton(fo));
+ } catch (FileStateInvalidException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return img;
+ }
+
+ final Action[] getActions() {
+ FileObject actions = Repository.getDefault().getDefaultFileSystem().findResource(
+ "Loaders/" + mimeType + "/Actions"
+ );
+ if (actions != null) {
+ DataFolder folder = DataFolder.findFolder(actions);
+ try {
+ return (Action[]) new DataLdrActions(folder, null).instanceCreate();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ } catch (ClassNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return DataLoaderPool.getDefaultFileLoader().getSwingActions();
+ }
+}
diff -r 54f623a0e6e1 -r 049d4cfc8b19 openide.loaders/test/unit/src/org/openide/loaders/DataLoaderInLayerTest.java
--- a/openide.loaders/test/unit/src/org/openide/loaders/DataLoaderInLayerTest.java Tue Jun 03 15:47:50 2008 -0700
+++ b/openide.loaders/test/unit/src/org/openide/loaders/DataLoaderInLayerTest.java Fri Jun 06 17:19:40 2008 +0200
@@ -42,12 +42,22 @@ package org.openide.loaders;
package org.openide.loaders;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
import org.openide.filesystems.*;
import java.io.IOException;
import java.util.*;
import org.netbeans.junit.*;
import java.beans.PropertyChangeListener;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import javax.swing.Action;
import junit.framework.Test;
+import org.openide.actions.EditAction;
+import org.openide.nodes.Node;
+import org.openide.util.Utilities;
/** Check what can be done when registering loaders in layer.
* @author Jaroslav Tulach
@@ -93,6 +103,20 @@ public class DataLoaderInLayerTest exten
}
}
}
+ private static void addRemove(String mime, F factory, boolean add) throws IOException {
+ String res = "Loaders/" + mime + "/Factories/" + factory.getClass().getSimpleName().replace('.', '-') + ".instance";
+ FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot();
+ if (add) {
+ FileObject fo = FileUtil.createData(root, res);
+ fo.setAttribute("instanceCreate", factory);
+ assertSame("No serialization, just memory fs is used", factory, fo.getAttribute("instanceCreate"));
+ } else {
+ FileObject fo = root.getFileObject(res);
+ if (fo != null) {
+ fo.delete();
+ }
+ }
+ }
public void testSimpleGetChildren() throws Exception {
DataLoader l = DataLoader.getLoader(SimpleUniFileLoader.class);
@@ -122,6 +146,92 @@ public class DataLoaderInLayerTest exten
assertEquals(SimpleDataObject.class, dob.getClass());
} finally {
addRemove("text/plain", SimpleFactory.class, false);
+ }
+ }
+
+ public void testFactoryInstanceRegistrationWorksAsWell() throws Exception {
+ URL u = DataLoaderInLayerTest.class.getResource("/org/openide/loaders/saveAll.gif");
+ Image img = Toolkit.getDefaultToolkit().createImage(u);
+
+ DataObject.Factory f = DataLoaderPool.factory(SimpleDataObject.class, "text/simplefactory", img);
+
+ addRemove("text/plain", f, true);
+ try {
+ FileSystem lfs = createFS("folderF/file.simple");
+ FileObject fo = lfs.findResource("folderF");
+ DataFolder df = DataFolder.findFolder(fo);
+ DataObject[] arr = df.getChildren();
+ assertEquals("One object", 1, arr.length);
+ DataObject dob = arr[0];
+ assertEquals(SimpleDataObject.class, dob.getClass());
+
+ FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot();
+ FileObject edit = FileUtil.createData(root, "/Loaders/text/simplefactory/Actions/org-openide-actions-EditAction.instance");
+
+ Node node = dob.getNodeDelegate();
+ Action[] actions = node.getActions(true);
+ assertEquals("One action is present: " + Arrays.asList(actions), 1, actions.length);
+ assertEquals("It is the edit one", EditAction.class, actions[0].getClass());
+
+ assertSame("Icon is propagated for open", img, node.getOpenedIcon(0));
+ assertSame("Icon is propagated", img, node.getIcon(0));
+
+ Reference ref = new WeakReference(df);
+ df = null;
+ assertGC("Folder can go away", ref);
+
+ df = DataFolder.findFolder(fo);
+ arr = df.getChildren();
+ assertEquals("One object", 1, arr.length);
+ assertEquals("Object is the same", dob, arr[0]);
+ } finally {
+ addRemove("text/plain", f, false);
+ }
+ }
+
+ public void testFactoryInstanceRegistrationWorksAsWellNowFromLayer() throws Exception {
+ URL u = DataLoaderInLayerTest.class.getResource("/org/openide/loaders/saveAll.gif");
+ FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot();
+ FileObject instance = FileUtil.createData(root, "TestLoaders/text/L.instance");
+ instance.setAttribute("dataObjectClass", SimpleDataObject.class.getName());
+ instance.setAttribute("mimeType", "text/simplefactory");
+ instance.setAttribute("SystemFileSystem.icon", u);
+
+
+ Image img = Utilities.loadImage("org/openide/loaders/saveAll.gif");
+
+ DataObject.Factory f = DataLoaderPool.factory(instance);
+
+ addRemove("text/plain", f, true);
+ try {
+ FileSystem lfs = createFS("folderQ/file.simple");
+ FileObject fo = lfs.findResource("folderQ");
+ DataFolder df = DataFolder.findFolder(fo);
+ DataObject[] arr = df.getChildren();
+ assertEquals("One object", 1, arr.length);
+ DataObject dob = arr[0];
+ assertEquals(SimpleDataObject.class, dob.getClass());
+
+ FileObject edit = FileUtil.createData(root, "/Loaders/text/simplefactory/Actions/org-openide-actions-EditAction.instance");
+
+ Node node = dob.getNodeDelegate();
+ Action[] actions = node.getActions(true);
+ assertEquals("One action is present: " + Arrays.asList(actions), 1, actions.length);
+ assertEquals("It is the edit one", EditAction.class, actions[0].getClass());
+
+ assertImage("Icon is propagated for open", img, node.getOpenedIcon(0));
+ assertImage("Icon is propagated", img, node.getIcon(0));
+
+ Reference ref = new WeakReference(df);
+ df = null;
+ assertGC("Folder can go away", ref);
+
+ df = DataFolder.findFolder(fo);
+ arr = df.getChildren();
+ assertEquals("One object", 1, arr.length);
+ assertEquals("Object is the same", dob, arr[0]);
+ } finally {
+ addRemove("text/plain", f, false);
}
}
@@ -311,5 +421,25 @@ public class DataLoaderInLayerTest exten
supp.remove (l);
}
}
-
+
+ private static void assertImage(String msg, Image img1, Image img2) {
+ ImageObserver obs = new ImageObserver() {
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
+ fail("Already updated, hopefully");
+ return true;
+ }
+ };
+
+ int h, w;
+ assertEquals("Width: " + msg, w = img1.getWidth(obs), img2.getWidth(obs));
+ assertEquals("Height: " + msg, h = img1.getHeight(obs), img2.getHeight(obs));
+
+
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ //assertEquals("Pixel " + i + ", " + j + " same: " + msg, img1.get)
+ }
+ }
+
+ }
}