alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.loaders;

import java.io.*;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

import org.openide.*;
import org.openide.cookies.*;
import org.openide.filesystems.*;
import org.openide.filesystems.FileSystem; // override java.io.FileSystem
import org.openide.loaders.*;
import org.openide.modules.ModuleInfo;
import org.openide.util.*;
import org.openide.util.Utilities;
import org.openide.util.actions.*;
import org.openide.nodes.*;
import java.util.List;

/** A data object whose only purpose is to supply InstanceCookie.
* The instances are created by default instantiation; the name of the class
* to instantiate is stored on disk, typically right in the file name.
* 

This data object is generally used to configure menus and toolbars, * though it could be used in any situation requiring instances to be present in * a folder; for example, anything using {@link FolderInstance}. *

Typical instance classes are subclasses of {@link SystemAction} to make * menu items or toolbar buttons; {@link javax.swing.JSeparator} for a menu * separator; or {@link javax.swing.JToolBar.Separator} for a toolbar * separator. *

Use {@link #create} and {@link #remove} to make the objects. * Better yet, use an XML filesystem to install them declaratively. *

* Instance data object by default recognizes all files with .instance * suffix. Such file can have associated optional file attributes: *

* *
instanceCreate
instantionalized Object (e.g. created by * methodvalue at XML filesystem) *
instanceOf
String that is tokenized at ':', ',', ';' and * whitespace boundaries. Resulting tokens represent class names that created * instance is instanceof. Utilizing it may improve performance. *
* (optional file attributes documented since 3.34). * * @author Ian Formanek */ public class InstanceDataObject extends MultiDataObject implements InstanceCookie.Of { /** generated Serialized Version UID */ static final long serialVersionUID = -6134784731744777123L; private static final String EA_INSTANCE_CLASS = "instanceClass"; // NOI18N private static final String EA_INSTANCE_CREATE = "instanceCreate"; // NOI18N private static final String EA_INSTANCE_OF = "instanceOf"; // NOI18N /** data object name cached in the attribute to prevent instance creation when its InstanceNode is displayed. */ static final String EA_NAME = "name"; // NOI18N /** if an instance is modified, what is the delay before it is saved? */ private static final int SAVE_DELAY = 2000; // XXX #27494 Please changes to these two fields apply also into // core/naming/src/org/netbeans/core/naming/Utils class. /** opening symbol */ private static final char OPEN = '['; /** closing symbol */ private static final char CLOSE = ']'; /** File extension for instance data objects. */ public static final String INSTANCE = "instance"; // NOI18N /** File extension for serialized files. */ static final String SER_EXT = "ser"; // NOI18N /** File extension for xml settings. */ static final String XML_EXT = "settings"; //NOI18N /** optional property file key for icon resource */ private static final String ICON_NAME = "icon"; // NOI18N /** the object that handles instance cookie manipulation */ private Ser ser; /** saving task status */ private boolean savingCanceled = false; private static final RequestProcessor PROCESSOR = new RequestProcessor ("Instance processor"); // NOI18N private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.loaders.InstanceDataObject"); // NOI18N /** Create a new instance. * Do not use this to make instances; use {@link #create}. * @param pf primary file object for this data object * @param loader the loader * @throws DataObjectExistsException if it already exists */ public InstanceDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException { super (pf, loader); if (pf.hasExt (SER_EXT)) { // NOI18N // only if we have ser extension the Ser (InstanceCookie) // should be accessible ser = new Ser (this); getCookieSet ().add (ser); // I.e. never create one. } else if (!pf.hasExt(XML_EXT)) { // otherwise we implement just the InstanceCookie directly ser = new Ser (this); } } /** used for synchronization instead of the IDO object */ private final Object IDO_LOCK = new Object(); /** Get the lock of the IDO object. Do not synchronize on the IDO object * directly to prevent deadlocks. */ private Object getLock() { return IDO_LOCK; } /** Finds file object of specified name in a given folder. * @param folder the folder where search * @param name the name to give to the object (can be null if no special name besides the class name is needed) * @param className the name of the class the new object should provide an instance of * @return the found file object or null if it does not exist */ private static FileObject findFO (DataFolder folder, String name, String className) { FileObject fo = folder.getPrimaryFile (); String classNameEnc = className.replace ('.', '-'); Enumeration en = fo.getChildren(false); FileObject newFile; while (en.hasMoreElements()) { newFile = (FileObject) en.nextElement(); if (!newFile.hasExt(INSTANCE)) continue; if (name != null) { if (!name.equals(getName(newFile))) continue; } else { if (!classNameEnc.equals(getName(newFile))) continue; } if (className.equals(InstanceDataObject.Ser.getClassName(newFile))) { return newFile; } } return null; } /** get data object name from specified file object */ private static String getName(FileObject fo) { String superName = (String) fo.getAttribute(EA_NAME); if (superName != null) return superName; superName = fo.getName(); int bracket = superName.indexOf (OPEN); if (bracket == -1) { return unescape(superName); } else { warnAboutBrackets(fo); return unescape(superName.substring(0, bracket)); } } /** Finds instance of specified name in a given folder. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * @param className the name of the class the new object should provide an instance of * @return the found instance data object or null if it does not exist */ public static InstanceDataObject find (DataFolder folder, String name, String className) { FileObject newFile = findFO (folder, name, className); if (newFile != null) { try { return (InstanceDataObject)DataObject.find (newFile); } catch (DataObjectNotFoundException e) { } } return null; } /** Finds instance of specified name in a given folder. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * @param clazz the class to create instance for (see class header for details) * @return the found instance data object or null if it does not exist */ public static InstanceDataObject find (DataFolder folder, String name, Class clazz) { return find (folder, name, clazz.getName ()); } /** Create a new InstanceDataObject in a given folder. If object with specified name already exists, it is returned. * You should specify the name if there is a chance another file of the same * instance class already exists in the folder; or just to provide a more * descriptive name, which will appear in the Explorer for example. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * but name cannot be empty * @param className the name of the class the new object should provide an instance of (see class header for details) * @return the newly created or existing instance data object * @exception IOException if the file cannot be created */ public static InstanceDataObject create (DataFolder folder, final String name, final String className) throws IOException { final FileObject fo = folder.getPrimaryFile (); if (name != null && name.length() == 0) { throw new IOException("name cannot be empty"); // NOI18N } FileObject newFile = findFO (folder, name, className); if (newFile == null) { final FileObject[] fos = new FileObject[1]; DataObjectPool.getPOOL().runAtomicAction (fo, new FileSystem.AtomicAction() { public void run () throws IOException { String fileName; if (name == null) { fileName = FileUtil.findFreeFileName( fo, className.replace ('.', '-'), INSTANCE); } else { fileName = escape(name); } fos[0] = fo.createData (fileName, INSTANCE); fos[0].setAttribute(EA_INSTANCE_CLASS, className); } }); newFile = fos[0]; } return (InstanceDataObject)DataObject.find (newFile); } /** Create a new InstanceDataObject in a given folder. If object with specified name already exists, it is returned. * You should specify the name if there is a chance another file of the same * instance class already exists in the folder; or just to provide a more * descriptive name, which will appear in the Explorer for example. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * @param clazz the class to create instance for (see class header for details) * @return the newly created or existing instance data object * @exception IOException if the file cannot be created */ public static InstanceDataObject create (DataFolder folder, String name, Class clazz) throws IOException { return create (folder, name, clazz.getName ()); } /** Create a new InstanceDataObject containing settings * in a given folder. If object with specified name already exists, it is returned. * If the module info is null then the origin module info * of an instance class is tried to find out. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * but name cannot be empty * @param instance the serializable instance * @param info the module info describing the settings provenance (can be null) * @return the newly created or existing instance data object * @exception IOException if the file cannot be created * @since 1.28 */ public static InstanceDataObject create (DataFolder folder, String name, Object instance, ModuleInfo info) throws IOException { return create(folder, name, instance, info, false); } /** Create a new InstanceDataObject containing settings * in a given folder. * If the module info is null then the origin module info * of an instance class is tried to find out. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to create the instance data object in * @param name the name to give to the object (can be null if no special name besides the class name is needed) * but name cannot be empty * @param instance the serializable instance * @param info the module info describing the settings provenance (can be null) * @param create true - always create new file; false * - store to existing file if exist * @return the newly created or existing instance data object * @exception IOException if the file cannot be created * @since 2.9 */ public static InstanceDataObject create (DataFolder folder, String name, Object instance, ModuleInfo info, boolean create) throws IOException { if (name != null && name.length() == 0) { throw new IOException("name cannot be empty"); // NOI18N } return Creator.createInstanceDataObject (folder, name, instance, info, create); } private static InstanceDataObject storeSettings (DataFolder df, String name, Object obj, ModuleInfo mi) throws IOException { FileObject fo = df.getPrimaryFile (); FileObject newFile = fo.getFileObject (name, XML_EXT); String fullname = fo.getPath() + '/' + name + '.' + XML_EXT; InstanceDataObject ido; boolean attachWithSave = false; try { if (newFile == null) { System.setProperty("InstanceDataObject.current.file", fo.getPath() + "/" + name + "." + XML_EXT); // NOI18N final ByteArrayOutputStream buf = storeThroughConvertor(obj, new FileObjectContext(fo, name)); System.setProperty("InstanceDataObject.current.file", ""); // NOI18N createdIDOs.add(fullname); newFile = fo.createData (name, XML_EXT); FileLock flock = null; try { flock = newFile.lock(); OutputStream os = newFile.getOutputStream(flock); os.write(buf.toByteArray()); os.close(); } finally { if (flock != null) flock.releaseLock(); } } else attachWithSave = true; ido = (InstanceDataObject)DataObject.find (newFile); // attachToConvertor will store the object ido.attachToConvertor(obj, attachWithSave); } finally { createdIDOs.remove(fullname); } return ido; } /** Remove an existing instance data object. * If you have the exact file name, just call {@link DataObject#delete}; * this method lets you delete an instance you do not have an exact record * of the file name for, based on the same information used to create it. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to remove the file from * @param name the name of the instance (can be null) * @param className the name of class the object referred to (see class header for details) * @return true if the instance was succesfully removed, false if not */ public static boolean remove (DataFolder folder, String name, String className) { FileLock lock = null; try { FileObject fileToRemove = findFO (folder, name, className); if (fileToRemove == null) // file not found return false; lock = fileToRemove.lock(); fileToRemove.delete(lock); } catch (IOException exc) { // something is bad, instance wasn't removed return false; } finally { if (lock != null) lock.releaseLock(); } return true; } /** Remove an existing instance data object. * If you have the exact file name, just call {@link DataObject#delete}; * this method lets you delete an instance you do not have an exact record * of the file name for, based on the same information used to create it. *

Note: use of XML layers to install instances is generally preferred. * @param folder the folder to remove the file from * @param name the name of the instance (can be null) * @param clazz the class the object referred to (see class header for details) * @return true if the instance was succesfully removed, false if not */ public static boolean remove (DataFolder folder, String name, Class clazz) { return remove (folder, name, clazz.getName ()); } /* Help context for this object. * @return help context */ public HelpCtx getHelpCtx () { HelpCtx test = InstanceSupport.findHelp (this); if (test != null) return test; else return new HelpCtx (InstanceDataObject.class); } /** Little utility method for posting an exception * to the default ErrorManager with severity * ErrorManager.INFORMATIONAL */ static void inform(Throwable t) { err.notify(ErrorManager.INFORMATIONAL, t); } /* Provides node that should represent this data object. When a node for representation * in a parent is requested by a call to getNode (parent) it is the exact copy of this node * with only parent changed. This implementation creates instance * DataNode. *

* This method is called only once. * * @return the node representation for this data object * @see DataNode */ protected Node createNodeDelegate () { if (getPrimaryFile().hasExt(XML_EXT)) { un = new UpdatableNode(createNodeDelegateImpl()); return un; } else { return createNodeDelegateImpl(); } } private UpdatableNode un; /** allows to swap original node */ private final class UpdatableNode extends FilterNode { public UpdatableNode(Node n) { super(n); } public void update() { Children.MUTEX.postWriteRequest(new Runnable() { public void run() { changeOriginal(createNodeDelegateImpl(), true); } }); } } /** create node delegate */ private Node createNodeDelegateImpl () { try { if (getPrimaryFile().getFileSystem() != Repository.getDefault().getDefaultFileSystem()) { return new DataNode(this, Children.LEAF); } } catch (FileStateInvalidException ex) { inform(ex); return new DataNode(this, Children.LEAF); } if (getPrimaryFile().hasExt(XML_EXT)) { // if lookup does not contain any InstanceCookie then the object // is considered as unregognized if (null == getCookieFromEP(InstanceCookie.class)) { return new CookieAdjustingFilter(new UnrecognizedSettingNode()); } Node n = (Node) getCookieFromEP(Node.class); if (n != null) return new CookieAdjustingFilter(n); } // Instances of Node or Node.Handle should be used as is. try { if (instanceOf (Node.class)) { Node n = (Node)instanceCreate (); return new CookieAdjustingFilter(n); } else if (instanceOf (Node.Handle.class)) { Node.Handle h = (Node.Handle) instanceCreate (); return new CookieAdjustingFilter(h.getNode()); } } catch (IOException ex) { inform(ex); } catch (ClassNotFoundException ex) { inform(ex); } return new InstanceNode (this); } /** Node presents IDO as unregonized setting object which can be just deleted. */ private final class UnrecognizedSettingNode extends AbstractNode { public UnrecognizedSettingNode() { super(Children.LEAF); setName(NbBundle.getMessage(InstanceDataObject.class, "LBL_BrokenSettings")); //NOI18N setIconBase("org/openide/loaders/instanceBroken"); //NOI18N setShortDescription(InstanceDataObject.this.getPrimaryFile().toString()); } public boolean canDestroy() { return true; } public boolean canCut() { return false; } public boolean canCopy() { return false; } public boolean canRename() { return false; } public void destroy() throws IOException { InstanceDataObject.this.delete(); } protected SystemAction[] createActions() { return new SystemAction[] {SystemAction.get(org.openide.actions.DeleteAction.class)}; } } /** A filter which ensures that when some-random-class-impl-Node.instance * is created, its node delegate gives itself as the cookie for DataObject, * and not some other unrelated data object. E.g. Services/.../TemplatesNode.instance * vs. Templates/ and similar. See DataNodeTest unit test. */ private final class CookieAdjustingFilter extends FilterNode { public CookieAdjustingFilter(Node n) { super(n, null, new org.openide.util.lookup.ProxyLookup (new org.openide.util.Lookup[] { n.getLookup (), org.openide.util.lookup.Lookups.singleton(InstanceDataObject.this) })); } // If this node is used as the root of a new Explorer window etc., // just save the real underlying node; no need to make it a CAF later. public Node.Handle getHandle() { return getOriginal().getHandle(); } // #17920: Index cookie works only when equality works public boolean equals(Object o) { return this == o || getOriginal().equals(o) || (o != null && o.equals(getOriginal())); } public int hashCode() { return getOriginal().hashCode(); } } /** delegate .getCookie to Environment.Provider */ private Object getCookieFromEP(Class clazz) { //updateLookup(false); return getCookiesLookup().lookup(clazz); } void notifyFileChanged(FileEvent fe) { super.notifyFileChanged(fe); if (getPrimaryFile().hasExt(XML_EXT)) { if (!Creator.isFiredFromMe(fe)) { getCookiesLookup(true); } } } /* Serve up editor cookies where requested. */ public Node.Cookie getCookie(Class clazz) { Node.Cookie supe = null; if (getPrimaryFile().hasExt(XML_EXT)) { // #24683 fix: do not return any cookie until the .settings file is written // successfully; PROP_COOKIE is fired when cookies are available. String filename = getPrimaryFile().getPath(); if (createdIDOs.contains(filename)) return null; supe = (Node.Cookie) getCookieFromEP(clazz); if (InstanceCookie.class.isAssignableFrom(clazz)) return supe; } if (supe == null) supe = super.getCookie(clazz); return supe; } private Lookup.Result cookieResult = null; private Lookup.Result nodeResult = null; private Lookup cookiesLkp = null; private LookupListener cookiesLsnr = null; private LookupListener nodeLsnr = null; private Lookup getCookiesLookup() { return getCookiesLookup(false); } private Lookup getCookiesLookup(boolean reinit) { synchronized (getLock()) { if (!reinit && cookiesLkp != null) { return cookiesLkp; } } Lookup envLkp = Environment.findForOne(InstanceDataObject.this); synchronized (getLock()) { if (cookiesLkp == null || envLkp == null || !envLkp.getClass().equals(cookiesLkp.getClass())) { cookiesLkp = (envLkp == null) ? Lookup.EMPTY : envLkp; initCookieResult(); initNodeResult(); } } if (nodeResult != null) nodeResult.allItems(); if (cookieResult != null) cookieResult.allItems(); return cookiesLkp; } private void initNodeResult() { if (nodeResult != null && nodeLsnr != null) { nodeResult.removeLookupListener(nodeLsnr); } if (cookiesLkp != null && !cookiesLkp.equals(Lookup.EMPTY)) { nodeResult = cookiesLkp.lookup(new Lookup.Template(InstanceCookie.class)); nodeLsnr = new LookupListener() { public void resultChanged(LookupEvent lookupEvent) { if (InstanceDataObject.this.un != null) { un.update(); } } }; nodeResult.addLookupListener(nodeLsnr); } } private void initCookieResult() { if (cookieResult != null && cookiesLsnr != null) { cookieResult.removeLookupListener(cookiesLsnr); } if (cookiesLkp != null && !cookiesLkp.equals(Lookup.EMPTY)) { cookieResult = cookiesLkp.lookup(new Lookup.Template(Node.Cookie.class)); cookiesLsnr = new LookupListener() { public void resultChanged(LookupEvent lookupEvent) { firePropertyChange(DataObject.PROP_COOKIE, null, null); } }; cookieResult.addLookupListener(cookiesLsnr); } } /** Finds delegate instance cookie/if provided in cookie set. * @return instance cookie or null */ private InstanceCookie.Of delegateIC () { //return ser; InstanceCookie.Of ic = null; if (getPrimaryFile().hasExt(XML_EXT)) { ic = (InstanceCookie.Of) getCookieFromEP(InstanceCookie.Of.class); } else { ic = ser; } return ic; } /* The name of the bean for this file or null if the class name is not encoded * in the file name and rather the CLASS_NAME property from the file content should be used. * * @return the name for the instance or null if the class name is not defined in the name */ public String instanceName () { InstanceCookie delegateIC = delegateIC (); if (delegateIC == null) return this.getName(); return delegateIC.instanceName (); } /* The class of the instance represented by this cookie. * Can be used to test whether the instance is of valid * class before it is created. * * @return the class of the instance * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { InstanceCookie delegateIC = delegateIC (); if (delegateIC == null) return this.getClass(); return delegateIC.instanceClass (); } /** Query if this instance can create object of given type. * @param type the type to create * @return true or false */ public boolean instanceOf (Class type) { InstanceCookie.Of delegateIC = delegateIC (); if (delegateIC == null) return type.isAssignableFrom(this.getClass()); return delegateIC.instanceOf (type); } /* * @return an object to work with * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { InstanceCookie delegateIC = delegateIC (); if (delegateIC == null) return this; return delegateIC.instanceCreate (); } /** Checks whether the instance was created by this object. */ final boolean creatorOf (Object inst) { InstanceCookie delegateIC = delegateIC (); if (delegateIC instanceof Ser) { return ((Ser)delegateIC).creatorOf (inst); } return false; } /* Overriden to return only first part till the bracket */ public String getName () { String superName = (String) getPrimaryFile().getAttribute(EA_NAME); if (superName != null) return superName; superName = super.getName(); int bracket = superName.indexOf (OPEN); if (bracket == -1) { return unescape(superName); } else { warnAboutBrackets(getPrimaryFile()); return unescape(superName.substring(0, bracket)); } } private static final Set warnedAboutBrackets = new WeakSet(100); // Set /** Make sure people stop using this syntax eventually. * It is better to use the file attribute, not least because some VMs * do not much like [] in file names (OpenVMS had problems at one point, e.g.). */ private static void warnAboutBrackets(FileObject fo) { if (warnedAboutBrackets.add(fo)) { err.log(ErrorManager.WARNING, "Use of [] in " + fo + " is deprecated."); // NOI18N err.log(ErrorManager.WARNING, "(Please use the string-valued file attribute instanceClass instead.)"); // NOI18N } } // [PENDING] probably setName also needs to be overridden! /* Renames all entries and changes their files to new ones. */ protected FileObject handleRename (String name) throws IOException { FileObject fo = getPrimaryFile(); fo.setAttribute(EA_NAME, name); return fo; } // SEE ALSO org.netbeans.core.windows.util.WindowUtils FOR COPIED IMPL OF escape/unescape: // XXX #27494 Please changes to this method apply also into // core/naming/src/org/netbeans/core/naming/Utils class. /** Hex-escapes anything potentially nasty in some text. * Package-private for the benefit of the test suite. */ static String escape (String text) { boolean spacenasty = text.startsWith(" ") || text.endsWith(" ") || text.indexOf(" ") != -1; // NOI18N int len = text.length (); StringBuffer escaped = new StringBuffer (len); for (int i = 0; i < len; i++) { char c = text.charAt (i); // For some reason Windoze throws IOException if angle brackets in filename... if (c == '/' || c == ':' || c == '\\' || c == OPEN || c == CLOSE || c == '<' || c == '>' || // ...and also for some other chars (#16479): c == '?' || c == '*' || c == '|' || (c == ' ' && spacenasty) || c == '.' || c == '"' || c < '\u0020' || c > '\u007E' || c == '#') { // Hex escape. escaped.append ('#'); String hex = Integer.toString (c, 16).toUpperCase (); if (hex.length () < 4) escaped.append ('0'); if (hex.length () < 3) escaped.append ('0'); if (hex.length () < 2) escaped.append ('0'); escaped.append (hex); } else { escaped.append (c); } } return escaped.toString (); } /** Removes hex escapes and regenerates displayable Unicode. */ static String unescape (String text) { int len = text.length (); StringBuffer unesc = new StringBuffer (len); for (int i = 0; i < len; i++) { char c = text.charAt (i); if (c == '#') { if (i + 4 >= len) { err.log(ErrorManager.WARNING, "trailing garbage in instance name: " + text); // NOI18N break; } try { char[] hex = new char[4]; text.getChars (i + 1, i + 5, hex, 0); unesc.append ((char) Integer.parseInt (new String (hex), 16)); } catch (NumberFormatException nfe) { err.notify(ErrorManager.INFORMATIONAL, nfe); } i += 4; } else { unesc.append (c); } } return unesc.toString (); } // XXX #27494 Please changes to this field apply also into // core/naming/src/org/netbeans/core/naming/Utils class. private final static int MAX_FILENAME_LENGTH = 50; // XXX #27494 Please changes to this method apply also into // core/naming/src/org/netbeans/core/naming/Utils class. /** escape a filename and map it to the name with max length MAX_FILENAME_LENGTH * @see issue #17186 */ static String escapeAndCut (String name) { int maxLen = MAX_FILENAME_LENGTH; String ename = escape(name); if (ename.length() <= maxLen) return ename; String hash = Integer.toHexString(ename.hashCode()); maxLen = (maxLen > hash.length()) ? (maxLen-hash.length()) / 2 :1; String start = ename.substring(0, maxLen); String end = ename.substring(ename.length() - maxLen); return start + hash + end; } /** schedule task to save the instance */ final void scheduleSave () { // just for .ser files if (isSavingCanceled() || !getPrimaryFile().hasExt(SER_EXT)) return; doFileLock(); ser.getSaveTask().schedule(SAVE_DELAY); } private FileLock fileLock; /** try to lock the primary file; may return null */ private FileLock doFileLock() { synchronized (getLock()) { if (fileLock != null) return fileLock; try { fileLock = getPrimaryFile().lock(); } catch (IOException ex) { err.annotate(ex, getPrimaryFile().toString()); inform(ex); } return fileLock; } } /** release the file lock if any was taken */ private void relaseFileLock() { synchronized (getLock()) { if (fileLock == null) return; fileLock.releaseLock(); fileLock = null; } } /* Creates new object from template. * @exception IOException */ protected DataObject handleCreateFromTemplate ( DataFolder df, String name ) throws IOException { try { if (getPrimaryFile().hasExt(XML_EXT)) { InstanceCookie ic = (InstanceCookie)this.getCookie(InstanceCookie.class); Object obj = ic.instanceCreate(); DataObject d = createSettingsFile(df, name, obj); // reset template instance to null attachToConvertor(null); return d; } else if ( (!getPrimaryFile().hasExt(INSTANCE)) && Serializable.class.isAssignableFrom( instanceClass()) ) { InstanceCookie ic = (InstanceCookie)this.getCookie(InstanceCookie.class); Object obj = ic.instanceCreate(); return DataObject.find( createSerFile( df, name, obj ) ); } } catch (ClassNotFoundException ex) { inform(ex); } return super.handleCreateFromTemplate(df, name); } /* Copy a service sanely. For settings and serializable beans, special * methods are used to write out the resulting files, and the name to * use is taken from the *display name* of the current file, as this is * what the user is accustomed to seeing (for ServiceType's especially). * @see Issue #16278 */ protected DataObject handleCopy(DataFolder df) throws IOException { try { if (getPrimaryFile ().hasExt(XML_EXT)) { InstanceCookie ic = (InstanceCookie)getCookie(InstanceCookie.class); if (ic != null) { Object obj = ic.instanceCreate(); InstanceDataObject ido = createSettingsFile( df, getNodeDelegate().getDisplayName(), obj); ido.attachToConvertor(null); return ido; } } else if ( (!getPrimaryFile().hasExt(INSTANCE)) && Serializable.class.isAssignableFrom(instanceClass()) ) { InstanceCookie ic = (InstanceCookie)getCookie(InstanceCookie.class); if (ic != null) { Object obj = ic.instanceCreate(); return DataObject.find(createSerFile( df, getNodeDelegate().getDisplayName(), obj)); } } } catch (ClassNotFoundException ex) { inform(ex); } return super.handleCopy(df); } /** Is the saving task already canceled? If yes do not schedule it again. */ private boolean isSavingCanceled() { return savingCanceled; } protected void dispose() { if (getPrimaryFile().hasExt(SER_EXT)) { savingCanceled = true; if (ser != null) { RequestProcessor.Task task = ser.getSaveTask(); if (task.getDelay() > 0 || ser.isSaving() && !task.isFinished()) { task.waitFinished(); } } relaseFileLock(); } else if (getPrimaryFile().hasExt(XML_EXT)) { SaveCookie s = (SaveCookie) getCookie(SaveCookie.class); try { if (s != null) s.save(); } catch (IOException ex) { //ignore } } super.dispose(); } protected void handleDelete() throws IOException { savingCanceled = true; if (getPrimaryFile().hasExt(XML_EXT)) { handleDeleteSettings(); return; } if (ser != null) { RequestProcessor.Task task = ser.getSaveTask(); task.cancel(); if (ser.isSaving() && !task.isFinished()) task.waitFinished(); } relaseFileLock(); super.handleDelete(); } private void handleDeleteSettings() throws IOException { SaveCookie s = (SaveCookie) getCookie(SaveCookie.class); try { if (s != null) s.save(); } catch (IOException ex) { // ignore } super.handleDelete(); } private InstanceDataObject createSettingsFile (DataFolder df, String name, Object obj) throws IOException { boolean isServiceType = false; String filename; // find name for new service type if (obj instanceof ServiceType) { isServiceType = true; ServiceType sr = (ServiceType) obj; name = name == null? sr.getName(): name; String stName = name; ServiceType.Registry r = (ServiceType.Registry)Lookup.getDefault().lookup(ServiceType.Registry.class); for (int i = 1; r.find(stName) != null; i++) { stName = new StringBuffer(name.length() + 2). append(name).append('_').append(i).toString(); } if (!stName.equals(sr.getName())) { // Do not modify the original! sr = sr.createClone(); obj = sr; sr.setName(stName); } filename = escapeAndCut(stName); } else { filename = (name == null)? getPrimaryFile ().getName (): escapeAndCut(name); } filename = FileUtil.findFreeFileName( df.getPrimaryFile (), filename, getPrimaryFile ().getExt () ); InstanceDataObject newFile = storeSettings(df, filename, obj, null); if (name != null && !isServiceType) { newFile.getPrimaryFile().setAttribute(EA_NAME, name); } return newFile; } private FileObject createSerFile( DataFolder df, String name, Object obj ) throws IOException { FileLock lock = null; OutputStream ostream = null; FileObject newFile = null; try { FileObject fo = df.getPrimaryFile (); if (name == null) { name = FileUtil.findFreeFileName( df.getPrimaryFile (), getPrimaryFile ().getName (), getPrimaryFile ().getExt () ); } newFile = fo.getFileObject (name, SER_EXT); if (newFile == null) newFile = fo.createData (name, SER_EXT); lock = newFile.lock (); ostream = newFile.getOutputStream(lock); ObjectOutputStream p = new ObjectOutputStream(ostream); p.writeObject(obj); p.flush(); } finally { if (ostream != null) ostream.close(); if (lock != null) lock.releaseLock (); } return newFile; } /** Support for serialized objects. */ private static final class Ser extends InstanceSupport implements Runnable { /** the reference to the bean, so it is created just once when used */ private java.lang.ref.Reference bean = new java.lang.ref.SoftReference (null); /** last time the bean was read from a file */ private long saveTime; /** Custom class loader */ private ClassLoader customClassLoader; private InstanceDataObject dobj; /** @param dobj IDO containing the serialized instance */ public Ser (InstanceDataObject dobj) { super (dobj.getPrimaryEntry()); customClassLoader = null; this.dobj = dobj; } public String instanceName () { // try the life object if any FileObject fo = entry ().getFile (); if (fo.lastModified ().getTime () <= saveTime) { Object o = bean.get (); if (o != null) { return o.getClass().getName(); } } if (!fo.hasExt (INSTANCE)) { return super.instanceName (); } return getClassName(fo); } /** get class name from specified file object*/ private static String getClassName(FileObject fo) { // first of all try "instanceClass" property of the primary file Object attr = fo.getAttribute (EA_INSTANCE_CLASS); if (attr instanceof String) { return org.openide.util.Utilities.translate((String) attr); } else if (attr != null) { err.log(ErrorManager.WARNING, "instanceClass was a " + attr.getClass().getName()); // NOI18N } attr = fo.getAttribute (EA_INSTANCE_CREATE); if (attr != null) { return attr.getClass().getName(); } // otherwise extract the name from the filename String name = fo.getName (); int first = name.indexOf (OPEN) + 1; if (first != 0) { warnAboutBrackets(fo); } int last = name.indexOf (CLOSE); if (last < 0) { last = name.length (); } // take only a part of the string if (first < last) { name = name.substring (first, last); } name = name.replace ('-', '.'); name = org.openide.util.Utilities.translate(name); //System.out.println ("Original: " + getPrimaryFile ().getName () + " new one: " + name); // NOI18N return name; } /** Uses cache to remember list of classes to them this object is * assignable. */ public Class instanceClass() throws IOException, ClassNotFoundException { Class type = super.instanceClass (customClassLoader); updateListOfClasses (type, entry ().getFile ()); return type; } /** Uses the cache to answer this question without loading the class itself, if the * cache exists. */ public boolean instanceOf (Class type) { // try the life object if any FileObject fo = entry ().getFile (); if (fo.lastModified ().getTime () <= saveTime) { Object o = bean.get (); if (o != null) { return type.isInstance (o); } } // else do checking of classes // null means no cache exists Boolean res = inListOfClasses (type, entry ().getFile ()); if (res == null) { // uses instanceClass and then assignableFrom return super.instanceOf (type); } return res.booleanValue (); } public Object instanceCreate () throws IOException, ClassNotFoundException { FileObject fo = entry ().getFile (); Object o; if (fo.lastModified ().getTime () <= saveTime) { o = bean.get (); } else { o = null; } if (o != null) { return o; } saveTime = fo.lastModified ().getTime (); if (saveTime < System.currentTimeMillis ()) { saveTime = System.currentTimeMillis (); } if (fo.hasExt (INSTANCE)) { // try to ask for instance creation attribute o = fo.getAttribute (EA_INSTANCE_CREATE); } if (o == null) { // try super method o = super.instanceCreate (); } // remember the created value bean = new java.lang.ref.SoftReference (o); return o; } /** Checks whether the instance was created by this object. */ final boolean creatorOf (Object inst) { java.lang.ref.Reference r = bean; return r != null && r.get () == inst; } public void run () { try { saving = true; runImpl(); } finally { dobj.relaseFileLock(); saving = false; } } /** Saves the bean to disk. */ private void runImpl () { Object bean = this.bean.get (); if (bean == null) { // nothing to save return; } try { FileLock lock = dobj.doFileLock(); if (lock == null) return; ObjectOutputStream oos = new ObjectOutputStream ( entry ().getFile ().getOutputStream (lock) ); try { oos.writeObject (bean); // avoid bean reloading saveTime = entry ().getFile ().lastModified ().getTime (); } finally { oos.close (); } } catch (IOException ex) { err.annotate (ex, NbBundle.getMessage ( InstanceDataObject.class, "EXC_CannotSaveBean", // NOI18N instanceName (), entry ().getFile ().getPath() )); err.notify (ex); } } /** Check whether a given class is in list of all classes assigned to fo. * @param type type to test * @param fo file object to check * @return true if the class is in the list of objects */ private static Boolean inListOfClasses (Class type, FileObject fo) { Object obj = fo.getAttribute (EA_INSTANCE_OF); if (obj instanceof String) { String typeName = type.getName (); StringTokenizer tok = new StringTokenizer ((String)obj, "\n\t ,;:"); // NOI18N while (tok.hasMoreTokens ()) { String t = tok.nextToken ().trim(); if (typeName.equals (t)) { // we know this class is in the list of otherclasses return Boolean.TRUE; } } return Boolean.FALSE; } else if (obj != null) { err.log(ErrorManager.WARNING, "instanceOf was a " + obj.getClass().getName()); // NOI18N } // means no cache exists return null; } /** Updates list of classes. * @param type the object to be stored * @param fo file object to store the attribute to */ private static void updateListOfClasses (Class type, FileObject fo) { StringBuffer sb = null; HashSet added; // check whether all already defined EA_INSTANCE_OF are valid boolean update; Object obj = fo.getAttribute (EA_INSTANCE_OF); if (obj instanceof String) { update = false; sb = new StringBuffer (1024); added = new HashSet (101); added.add ("java.lang.Object"); // NOI18N collectType (type, sb, added); StringTokenizer tok = new StringTokenizer ((String)obj, ",;:"); // NOI18N while (tok.hasMoreTokens ()) { if (!added.contains (tok.nextToken ().trim())) { update = true; break; } } } else { // attribute is missing update = false; } if (update) { try { fo.setAttribute (EA_INSTANCE_OF, sb.toString ()); } catch (IOException ex) { // ignore!? probably no reason to report it } } } /** Converts type to string. * @param type * @param sb string buffer to store * @param done already added class Set(String) * @return true if something was added to the buffer */ private static boolean collectType ( Class type, StringBuffer sb, HashSet added ) { if (type == null) { // can be null for interfaces return false; } String typeName = type.getName (); if (added.contains (typeName)) { return false; } added.add (typeName); // add superclasses if (collectType (type.getSuperclass (), sb, added)) { sb.append (','); } // add superinterfaces Class[] impls = type.getInterfaces (); for (int i = 0; i < impls.length; i++) { if (collectType (impls[i], sb, added)) { sb.append (','); } } sb.append (typeName); return true; } final void setCustomClassLoader(ClassLoader cl) { this.customClassLoader = cl; } /** save task */ private RequestProcessor.Task task; /** return the instance save task */ public RequestProcessor.Task getSaveTask() { if (task == null) { task = PROCESSOR.create(this); } return task; } /** save task is running */ private boolean saving = false; public boolean isSaving() { return saving; } } // end of Ser final void setCustomClassLoader(ClassLoader cl) { if (ser instanceof Ser) ((Ser) ser).setCustomClassLoader(cl); } /** Support for creating instances allowing identify the origin of file events * fired as a consequence of this creating. * Not thread safe. */ private static class Creator implements FileSystem.AtomicAction { private ModuleInfo mi = null; private DataFolder folder = null; private Object instance = null; private String name = null; private InstanceDataObject result = null; private boolean create; private final static Creator me = new Creator (); private Creator() { } public void run () throws IOException { FileObject fo = folder.getPrimaryFile (); String filename = name; if (filename == null) { filename = instance.getClass().getName().replace ('.', '-'); filename = FileUtil.findFreeFileName(fo, filename, XML_EXT); } else { String escapedFileName = escape(filename); // do not cut if such file already exist FileObject newFile = fo.getFileObject (escapedFileName, XML_EXT); if (newFile == null) { filename = escapeAndCut(filename); } else { filename = escapedFileName; } if (create /*|| (newFile == null && Utilities.isWindows()) */) { filename = FileUtil.findFreeFileName(fo, filename, XML_EXT); } } result = storeSettings(folder, filename, instance, mi); } /** see InstanceDataObject.create */ public static InstanceDataObject createInstanceDataObject ( DataFolder folder, String name, Object instance, ModuleInfo mi, boolean create) throws IOException { synchronized (me) { me.mi = mi; me.folder = folder; me.instance = instance; me.name = name; me.create = create; DataObjectPool.getPOOL().runAtomicActionSimple (folder.getPrimaryFile(), me); me.mi = null; me.folder = null; me.instance = null; me.name = null; InstanceDataObject result = me.result; me.result = null; return result; } } /** is file event originated by this Creator? */ public static boolean isFiredFromMe (FileEvent fe) { return fe.firedFrom(me); } } /** store object to strem using convertor */ private static ByteArrayOutputStream storeThroughConvertor(Object inst, FileObjectContext ctx) throws IOException { FileObject fo = resolveConvertor(inst); Object convertor = fo.getAttribute("settings.convertor"); // NOI18N if (convertor == null) throw new IOException("missing attribute settings.convertor"); // NOI18N ByteArrayOutputStream b = new ByteArrayOutputStream(1024); Writer w = new OutputStreamWriter(b, "UTF-8"); // NOI18N convertorWriteMethod(convertor, new WriterProvider(w, ctx), inst); w.close(); return b; } /** reflection for void write (java.io.Writer w, Objectinst) method */ private static void convertorWriteMethod(Object convertor, Writer w, Object inst) throws IOException { Throwable e = null; try { java.lang.reflect.Method method = convertor.getClass().getMethod( "write", // NOI18N new Class[] {java.io.Writer.class, Object.class}); method.setAccessible(true); method.invoke(convertor, new Object[] {w, inst}); } catch (NoSuchMethodException ex) { e = ex; } catch (IllegalAccessException ex) { e = ex; } catch (java.lang.reflect.InvocationTargetException ex) { e = ex.getTargetException(); if (e instanceof IOException) throw (IOException) e; } if (e != null) { throw (IOException) ErrorManager.getDefault().annotate( new IOException("Problem with Convertor.write method."), // NOI18N e); } } /** path where to find convertor/provider definition */ private final static String EA_PROVIDER_PATH = "settings.providerPath"; // NOI18N /** look up appropriate convertor according to obj */ private static FileObject resolveConvertor(Object obj) throws IOException { String prefix = "xml/memory"; //NOI18N FileSystem sfs = Repository.getDefault().getDefaultFileSystem(); FileObject memContext = sfs.findResource(prefix); if (memContext == null) throw new FileNotFoundException("SFS:xml/memory while converting a " + obj.getClass().getName()); //NOI18N String[] classes = new String[] {obj.getClass().getName(), Object.class.getName()}; for (int i = 0; i < classes.length; i++) { String convertorPath = new StringBuffer(200).append(prefix).append('/'). append(classes[i].replace('.', '/')).toString(); // NOI18N FileObject fo = sfs.findResource(convertorPath); if (fo != null) { String providerPath = (String) fo.getAttribute(EA_PROVIDER_PATH); if (providerPath == null) break; FileObject ret = sfs.findResource(providerPath); if (ret == null) { throw new FileNotFoundException("Invalid settings.providerPath under SFS/xml/memory/ for " + obj.getClass()); // NOI18N } else { return ret; } } } throw new FileNotFoundException("None convertor was found under SFS/xml/memory/ for " + obj.getClass()); //NOI18N } private void attachToConvertor(Object obj) throws IOException { attachToConvertor (obj, false); } /** propagate instance to convertor; obj can be null */ private void attachToConvertor(Object obj, boolean save) throws IOException { // InstanceCookie subclass has to implement // void setInstance(Object inst) Object ic = getCookiesLookup().lookup(InstanceCookie.class); if (ic == null) { throw new IllegalStateException( "Trying to store object " + obj // NOI18N + " which most probably belongs to already disabled module!");// NOI18N } convertorSetInstanceMethod(ic, obj, save); } /** reflection for void setInstance(Object inst) */ private static void convertorSetInstanceMethod(Object convertor, Object inst, boolean save) throws IOException { Exception e = null; try { java.lang.reflect.Method method = convertor.getClass().getMethod( "setInstance", // NOI18N new Class[] {Object.class, Boolean.TYPE}); method.setAccessible(true); method.invoke(convertor, new Object[] {inst, (save ? Boolean.TRUE : Boolean.FALSE)}); } catch (NoSuchMethodException ex) { e = ex; } catch (IllegalAccessException ex) { e = ex; } catch (java.lang.reflect.InvocationTargetException ex) { e = ex; if (ex.getTargetException() instanceof IOException) { throw (IOException) ex.getTargetException(); } } if (e != null) { ErrorManager.getDefault().annotate( e, "Problem with InstanceCookie.setInstance method: " + convertor.getClass()); // NOI18N inform(e); } } /** filenames list of just created files; sync purpose */ private static final List createdIDOs = java.util.Collections.synchronizedList(new java.util.ArrayList(1)); /** helper allowing a Writer to provide context via Lookup.Provider */ private static final class WriterProvider extends Writer implements Lookup.Provider { private final Writer orig; private final FileObjectContext ctx; private Lookup lookup; public WriterProvider(Writer w, FileObjectContext ctx) { this.orig = w; this.ctx = ctx; } public void close() throws IOException { orig.close(); } public void flush() throws IOException { orig.flush(); } public void write(char[] cbuf, int off, int len) throws IOException { orig.write(cbuf, off, len); } public Lookup getLookup() { if (lookup == null) { lookup = org.openide.util.lookup.Lookups.singleton(ctx); } return lookup; } } /** The Restricted FileObject implementation allowing to get just * read-only informations about name and location. It should prevent * any manipulation with file or its content. */ private static final class FileObjectContext extends FileObject { private static final String UNSUPPORTED = "The Restricted FileObject" + //NOI18N " implementation allowing to get just read-only informations about" + //NOI18N " name and location. It should prevent any manipulation with file" + //NOI18N " or its content."; //NOI18N private final FileObject fo; private final FileObject parent; private final String name; public FileObjectContext(FileObject parent, String name) { this.parent = parent; this.name = name; this.fo = parent.getFileObject(name, XML_EXT); } public void addFileChangeListener(FileChangeListener fcl) { throw new UnsupportedOperationException(UNSUPPORTED); } public FileObject createData(String name, String ext) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public FileObject createFolder(String name) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public void delete(FileLock lock) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public Object getAttribute(String attrName) { return fo == null? null: fo.getAttribute(attrName); } public java.util.Enumeration getAttributes() { return fo == null? org.openide.util.Enumerations.empty(): fo.getAttributes(); } public FileObject[] getChildren() { return new FileObject[0]; } public String getExt() { return InstanceDataObject.XML_EXT; //NOI18N } public FileObject getFileObject(String name, String ext) { return null; } public FileSystem getFileSystem() throws FileStateInvalidException { return parent.getFileSystem(); } public java.io.InputStream getInputStream() throws java.io.FileNotFoundException { throw new UnsupportedOperationException(UNSUPPORTED); } public String getName() { return name; } public java.io.OutputStream getOutputStream(FileLock lock) throws java.io.IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public FileObject getParent() { return parent; } public long getSize() { return fo == null? 0: fo.getSize(); } public boolean isData() { return true; } public boolean isFolder() { return false; } public boolean isReadOnly() { return parent.isReadOnly(); } public boolean isRoot() { return false; } public boolean isValid() { return fo == null? false: fo.isValid(); } public java.util.Date lastModified() { return fo == null? parent.lastModified(): fo.lastModified(); } public FileLock lock() throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public void removeFileChangeListener(FileChangeListener fcl) { throw new UnsupportedOperationException(UNSUPPORTED); } public void rename(FileLock lock, String name, String ext) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public void setAttribute(String attrName, Object value) throws IOException { throw new UnsupportedOperationException(UNSUPPORTED); } public void setImportant(boolean b) { throw new UnsupportedOperationException(UNSUPPORTED); } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.