All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.filesystems.FileSystem Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.openide.filesystems;

import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.*;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;

/**
 * Interface that provides basic information about a virtual filesystem.
 * 

Implementing classes should also have associated subclasses of {@link FileObject}. * But most subclasses will be of either {@link AbstractFileSystem} or {@link MultiFileSystem}.

*

When using the {@code org.netbeans.modules.masterfs} module, you do not need * to explicitly create any filesystems just to access disk files; use {@link FileUtil#toFileObject} * instead.

*

The system filesystem may be gotten from {@link FileUtil#getConfigRoot} and * normally includes XML layers created using {@link XMLFileSystem}.

*

Since version 7.1 you can dynamically change the content of the system filesystem. * Either by inserting your own implementation of a filesystem directly or by * registering so called {@link Repository.LayerProvider}. With the * {@link Repository.LayerProvider provider} you can show a dialog letting the user log in * to some server and only later make {@link XMLFileSystem additional XML layers} * available. *

* In case you need to insert a dynamic file system with various definitions (NetBeans Platform menus, * toolbars, layout of windows, etc.), you can create a filesystem implementation, * then register it in default lookup. * It is easiest to subclass {@link AbstractFileSystem} and implement * few simple query interfaces ({@link AbstractFileSystem.List}, * {@link AbstractFileSystem.Info}, {@link AbstractFileSystem.Change}). * For example:

*
{@code @}{@link ServiceProviders}({
    {@code @}{@link ServiceProvider}(service=FileSystem.class),
    {@code @}{@link ServiceProvider}(service=LoginFileSystem.class)
})
public class LoginFileSystem extends AbstractFileSystem implements
{@link AbstractFileSystem.List}, {@link AbstractFileSystem.Info}, {@link AbstractFileSystem.Change} {
    public LoginFileSystem() {
        this.info = this;
        this.change = this;
        this.list = this;
    }
    public static void initialize() throws SAXException {
        LoginFileSystem lfs = {@link Lookup}.getDefault().lookup(LoginFileSystem.class);
        lfs.initializeSomehow();
    }
     
    private void initializeSomehow() {
        // do some initialization
        super.refresh();
    } 
    
    public String[] children(String path) {
        // compute list of children somehow
    }
    
    // other method implementations follow...
}
 * 
*

Since version 7.3 you can also return {@link Boolean#TRUE} from a call to * {@code yourFS.getRoot().getAttribute("fallback")} so as to place your filesystem * behind all layers provided by standard modules.

*/ public abstract class FileSystem implements Serializable { static final Logger LOG = Logger.getLogger(FileSystem.class.getName()); /** generated Serialized Version UID */ private static final long serialVersionUID = -8931487924240189180L; /** Property name indicating validity of filesystem. */ public static final String PROP_VALID = "valid"; // NOI18N /** * Property name indicating whether filesystem is hidden. * @deprecated The property is now hidden. */ @Deprecated public static final String PROP_HIDDEN = "hidden"; // NOI18N /** * Property name giving internal system name of filesystem. * @deprecated This system name should now be avoided in favor of identifying files persistently by URL. */ @Deprecated public static final String PROP_SYSTEM_NAME = "systemName"; // NOI18N /** Property name giving display name of filesystem. * @since 2.1 */ public static final String PROP_DISPLAY_NAME = "displayName"; // NOI18N /** Property name giving root folder of filesystem. */ public static final String PROP_ROOT = "root"; // NOI18N /** Property name giving read-only state. */ public static final String PROP_READ_ONLY = "readOnly"; // NOI18N /** Used for synchronization purpose*/ private static final Object internLock = new Object(); private transient static ThreadLocal thrLocal = new ThreadLocal(); /** Empty status */ private static final StatusDecorator STATUS_NONE = new StatusDecorator() { public String annotateName(String name, Set files) { return name; } @Override public String annotateNameHtml(String name, Set files) { return null; } }; /** is this filesystem valid? * It can be invalid if there is another filesystem with the * same name in the filesystem pool. */ transient private boolean valid = false; /** True if the filesystem is assigned to pool. * Is modified from Repository methods. */ transient boolean assigned = false; /**Repository that contains this FileSystem or null*/ private transient Repository repository = null; private transient FCLSupport fclSupport; /** system name */ private String systemName = ""; // NOI18N /** Utility field used by event firing mechanism. */ private transient ListenerList fileStatusList; private transient ListenerList vetoableChangeList; private transient PropertyChangeSupport changeSupport; /** Default constructor. */ public FileSystem() { } /** Should check for external modifications. All existing FileObjects will be * refreshed. For folders it should reread the content of disk, * for data file it should check for the last time the file has been modified. * * The default implementation is to do nothing, in contradiction to the rest * of the description. Unless subclasses override it, the method does not work. * * @param expected should the file events be marked as expected change or not? * @see FileEvent#isExpected * @since 2.16 */ public void refresh(boolean expected) { } /** Test whether filesystem is valid. * Generally invalidity would be caused by a name conflict in the filesystem pool. * @return true if the filesystem is valid */ public final boolean isValid() { return valid; } /** Setter for validity. Accessible only from filesystem pool. * @param v the new value */ final void setValid(boolean v) { if (v != valid) { valid = v; firePropertyChange( PROP_VALID, (!v) ? Boolean.TRUE : Boolean.FALSE, v ? Boolean.TRUE : Boolean.FALSE, Boolean.FALSE ); } } /** Provides a name for the system that can be presented to the user. *

* This call should never be used to attempt to identify the file root * of the filesystem. On some systems it may happen to look the same but this is a * coincidence and may well change in the future. Either check whether * you are working with a {@link LocalFileSystem} or similar implementation and use * {@link LocalFileSystem#getRootDirectory}; or better, try * {@link FileUtil#toFile} which is designed to do this correctly. *

Note: for most purposes it is probably a bad idea to use * this method. Instead look at {@link FileUtil#getFileDisplayName}. * @return user presentable name of the filesystem */ public abstract String getDisplayName(); /** Internal (system) name of the filesystem. * Should uniquely identify the filesystem, as it will * be used during serialization of its files. The preferred way of doing this is to concatenate the * name of the filesystem type (e.g. the class) and the textual form of its parameters. *

* A change of the system name should be interpreted as a change of the internal * state of the filesystem. For example, if the root directory is moved to different * location, one should rebuild representations for all files * in the system. *

* This call should never be used to attempt to identify the file root * of the filesystem. On Unix systems it may happen to look the same but this is a * coincidence and may well change in the future. Either check whether * you are working with a {@link LocalFileSystem} or similar implementation and use * {@link LocalFileSystem#getRootDirectory}; or better, try * {@link FileUtil#toFile} which is designed to do this correctly. * @return string with system name * @deprecated The system name should now be avoided in favor of identifying files persistently by URL. */ @Deprecated public final String getSystemName() { return systemName; } /** Changes system name of the filesystem. * This property is bound and constrained: first of all * all vetoable listeners are asked whether they agree with the change. If so, * the change is made and all change listeners are notified of * the change. * *

Warning: this method is protected so that only subclasses can change * the system name. * * @param name new system name * @exception PropertyVetoException if the change is not allowed by a listener * @deprecated The system name should now be avoided in favor of identifying files persistently by URL. */ @Deprecated protected final void setSystemName(String name) throws PropertyVetoException { String o; String n; synchronized (PROP_SYSTEM_NAME) { if (systemName.equals(name)) { return; } // I must be the only one who works with system pool (that is listening) // on this interface fireVetoableChange(PROP_SYSTEM_NAME, systemName, name); o = systemName; n = systemName = name.intern(); } firePropertyChange(PROP_SYSTEM_NAME, o, n); /** backward compatibility for FileSystems that don`t fire * PROP_DISPLAY_NAME*/ firePropertyChange(PROP_DISPLAY_NAME, null, null); } /** * Caches the value of 'default' flag. It is extremely costly to call to FileUtil.getConfigRoot() * during startup, as the getter for repository waits for the rapidly changing default Lookup. * In a multi-context environment, the flag might be inappropriate for the current execution context; * an execution "A" could possibly reach an instance of default FS for execution "B". But as the two execution * should each operate with a different Repository with distinct instances of FileSystems, such condition * should be treated as an error. */ private volatile Boolean defFS; /** Returns true if the filesystem is default. * @return true if this is {@link Repository#getDefaultFileSystem} */ public final boolean isDefault() { boolean check = false; // XXX hotfix // assert check = true; if (defFS != null && !check) { return defFS; } FileSystem fs = null; try { fs = FileUtil.getConfigRoot().getFileSystem(); } catch (FileStateInvalidException ex) { Exceptions.printStackTrace(ex); } if (defFS != null) { // XXX hotfix // assert defFS == (this == fs) : "Default filesystem used in foreign execution"; } return defFS = (this == fs); } /** Test if the filesystem is read-only or not. * @return true if the system is read-only */ public abstract boolean isReadOnly(); /** Getter for root folder in the filesystem. * * @return root folder of whole filesystem */ public abstract FileObject getRoot(); /** Finds file in the filesystem by name. *

* The default implementation converts dots in the package name into slashes, * concatenates the strings, adds any extension prefixed by a dot and calls * the {@link #findResource findResource} method. * *

Note: when both of name and ext are null then name and * extension should be ignored and scan should look only for a package. * * @param aPackage package name where each package component is separated by a dot * @param name name of the file (without dots) or null if * one wants to obtain a folder (package) and not a file in it * @param ext extension of the file (without leading dot) or null if one needs * a package and not a file * * @return a file object that represents a file with the given name or * null if the file does not exist * @deprecated Please use the ClassPath API instead, or use {@link #findResource} if you are not interested in classpaths. */ @Deprecated public FileObject find(String aPackage, String name, String ext) { assert false : "Deprecated."; StringBuffer bf = new StringBuffer(); // append package and name if (!aPackage.equals("")) { // NOI18N String p = aPackage.replace('.', '/'); bf.append(p); bf.append('/'); } // append name if (name != null) { bf.append(name); } // append extension if there is one if (ext != null) { bf.append('.'); bf.append(ext); } return findResource(bf.toString()); } /** Finds a file given its full resource path. * @param name the resource path, e.g. "dir/subdir/file.ext" or "dir/subdir" or "dir" * @return a file object with the given path or * null if no such file exists */ public abstract FileObject findResource(String name); /** Returns temporary folder if it is avaliable on this file system. * Method never returns null. IOException is thrown instead. * @return a file object for temporary folder * @throws IOException * @since 7.60 */ public FileObject getTempFolder() throws IOException { throw new IOException("Unsupported operation"); // NOI18N } /** Creates temporary file in the given parent folder. * Method never returns null. IOException is thrown instead. * @param parent the parent folder where temporary file will be created * @param prefix prefix of the name of created file * @param suffix suffix of the name of created file * @param deleteOnExit delete file on exit * @return new temporary file * @throws IOException * @since 7.60 */ public FileObject createTempFile(FileObject parent, String prefix, String suffix, boolean deleteOnExit) throws IOException { throw new IOException("Unsupported operation"); // NOI18N } /** Finds various extensions for set of file objects coming from * this file system. * For example actions should be obtainable as:

     * actions = fs.{@link #findExtrasFor(java.util.Set) findExtrasFor}(foSet).{@link Lookup#lookupAll(java.lang.Class) lookupAll}({@link javax.swing.Action});
     * 
* @param objects the set of objects * @return the lookup providing various extensions (usually visual) * for these objects * @since 8.12 */ public Lookup findExtrasFor(Set objects) { return new FileExtrasLkp(this, objects); } /** Reads object from stream and creates listeners. * @param in the input stream to read from * @exception IOException error during read * @exception ClassNotFoundException when class not found */ @SuppressWarnings("deprecation") private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { in.defaultReadObject(); } @Override public String toString() { return getSystemName() + "[" + super.toString() + "]"; // NOI18N } private static volatile Lookup.Result statusResult; private static StatusDecorator defaultStatus() { if (statusResult == null) { statusResult = Lookup.getDefault().lookupResult(StatusDecorator.class); } Iterator it = statusResult.allInstances().iterator(); return it.hasNext() ? it.next() : SFS_STATUS; } /** * Gets a status object that can annotate a set of files by changing the names or icons * associated with them. *

* The default implementation returns a status object making no modifications, * unless this {@link #isDefault()} in which case certain special attributes * will be honored as of org.openide.filesystems 7.25: *

*
{@code displayName}
*
Value of {@link Status#annotateName}. Often used with {@code bundlevalue} in an {@link XMLFileSystem}.
*
{@code SystemFileSystem.localizingBundle}
*
Name of a bundle (as per {@link NbBundle#getBundle(String)}) in which to look up a display name. * The bundle key is the {@link FileObject#getPath}. * {@code displayName} is preferred for new code.
*
{@code iconBase}
*
Resource path to icon for {@link Status#annotateIcon}. * {@code _32} will be inserted before the file suffix for 32x32 icons.
*
{@code SystemFileSystem.icon} and {@code SystemFileSystem.icon32}
*
Icon specified directly as a {@link URL} (usually {@code nbresloc} protocol) * or {@link Image}. {@code iconBase} is preferred for new code.
*
* @return the status object for this filesystem */ public StatusDecorator getDecorator() { return isDefault() ? defaultStatus() : STATUS_NONE; } /** Executes atomic action. The atomic action represents a set of * operations constituting one logical unit. It is guaranteed that during * execution of such an action no events about changes in the filesystem * will be fired. *

* Warning: the action should not take a significant amount of time, and should finish as soon as * possible--otherwise all event notifications will be blocked. *

Warning: do not be misled by the name of this method; * it does not require the filesystem to treat the changes as an atomic block of * commits in the database sense! That is, if an exception is thrown in the middle * of the action, partial results will not be undone (in general this would be * impossible to implement for all filesystems anyway). * @param run the action to run * @exception IOException if there is an IOException thrown in the actions' {@link AtomicAction#run run} * method */ public final void runAtomicAction(final AtomicAction run) throws IOException { getEventControl().runAtomicAction(run); } /** * Begin of block, that should be performed without firing events. * Firing of events is postponed after end of block . * There is strong necessity to use always both methods: beginAtomicAction * and finishAtomicAction. It is recomended use it in try - finally block. * @param run Events fired from this atomic action will be marked as events * that were fired from this run. */ void beginAtomicAction(FileSystem.AtomicAction run) { getEventControl().beginAtomicAction(run); } void beginAtomicAction() { beginAtomicAction(null); } /** * End of block, that should be performed without firing events. * Firing of events is postponed after end of block . * There is strong necessity to use always both methods: beginAtomicAction * and finishAtomicAction. It is recomended use it in try - finally block. */ void finishAtomicAction() { getEventControl().finishAtomicAction(); } /** * Inside atomicAction adds an event dispatcher to the queue of FS events * and firing of events is postponed. If not event handlers are called directly. * @param run dispatcher to run */ void dispatchEvent(EventDispatcher run) { getEventControl().dispatchEvent(run); } private final EventControl getEventControl() { EventControl evnCtrl = thrLocal.get(); if (evnCtrl == null) { thrLocal.set(evnCtrl = new EventControl()); } return evnCtrl; } /** Registers FileStatusListener to receive events. * The implementation registers the listener only when getStatus () is * overriden to return a special value. * * @param listener The listener to register. */ public final void addFileStatusListener(FileStatusListener listener) { synchronized (internLock) { // JST: Ok? Do not register listeners when the fs cannot change status? if (getDecorator() == STATUS_NONE) { return; } if (fileStatusList == null) { fileStatusList = new ListenerList(); } fileStatusList.add(listener); } } /** Removes FileStatusListener from the list of listeners. *@param listener The listener to remove. */ public final void removeFileStatusListener(FileStatusListener listener) { if (fileStatusList == null) { return; } fileStatusList.remove(listener); } /** Notifies all registered listeners about change of status of some files. * * @param event The event to be fired */ protected final void fireFileStatusChanged(FileStatusEvent event) { if (fileStatusList == null) { return; } List listeners = fileStatusList.getAllListeners(); dispatchEvent(new FileStatusDispatcher(listeners, event)); } /** Adds listener for the veto of property change. * @param listener the listener */ public final void addVetoableChangeListener(VetoableChangeListener listener) { synchronized (internLock) { if (vetoableChangeList == null) { vetoableChangeList = new ListenerList(); } vetoableChangeList.add(listener); } } /** Removes listener for the veto of property change. * @param listener the listener */ public final void removeVetoableChangeListener(VetoableChangeListener listener) { if (vetoableChangeList == null) { return; } vetoableChangeList.remove(listener); } /** Fires property vetoable event. * @param name name of the property * @param o old value of the property * @param n new value of the property * @exception PropertyVetoException if an listener vetoed the change */ protected final void fireVetoableChange(String name, Object o, Object n) throws PropertyVetoException { if (vetoableChangeList == null) { return; } PropertyChangeEvent e = null; for (VetoableChangeListener l : vetoableChangeList.getAllListeners()) { if (e == null) { e = new PropertyChangeEvent(this, name, o, n); } l.vetoableChange(e); } } /** Registers PropertyChangeListener to receive events. *@param listener The listener to register. */ public final void addPropertyChangeListener(PropertyChangeListener listener) { synchronized (internLock) { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } } changeSupport.addPropertyChangeListener(listener); } /** Removes PropertyChangeListener from the list of listeners. *@param listener The listener to remove. */ public final void removePropertyChangeListener(PropertyChangeListener listener) { if (changeSupport != null) { changeSupport.removePropertyChangeListener(listener); } } /** Fires property change event. * @param name name of the property * @param o old value of the property * @param n new value of the property */ protected final void firePropertyChange(String name, Object o, Object n) { firePropertyChange(name, o, n, null); } final void firePropertyChange(String name, Object o, Object n, Object propagationId) { if (changeSupport == null) { return; } if ((o != null) && (n != null) && o.equals(n)) { return; } PropertyChangeEvent e = new PropertyChangeEvent(this, name, o, n); e.setPropagationId(propagationId); changeSupport.firePropertyChange(e); } /** Notifies this filesystem that it has been added to the repository. * Various initialization tasks could go here. The default implementation does nothing. *

Note that this method is advisory and serves as an optimization * to avoid retaining resources for too long etc. Filesystems should maintain correct * semantics regardless of whether and when this method is called. */ public void addNotify() { } /** Notifies this filesystem that it has been removed from the repository. * Concrete filesystem implementations could perform clean-up here. * The default implementation does nothing. *

Note that this method is advisory and serves as an optimization * to avoid retaining resources for too long etc. Filesystems should maintain correct * semantics regardless of whether and when this method is called. */ public void removeNotify() { } /** getter for Repository * @return Repository that contains this FileSystem or null if FileSystem * is not part of any Repository */ final Repository getRepository() { return repository; } void setRepository(Repository rep) { repository = rep; } final FCLSupport getFCLSupport() { synchronized (FCLSupport.class) { if (fclSupport == null) { fclSupport = new FCLSupport(); } } return fclSupport; } /** Add new listener to this object. * @param fcl the listener * @since 2.8 */ public final void addFileChangeListener(FileChangeListener fcl) { getFCLSupport().addFileChangeListener(fcl); } /** Remove listener from this object. * @param fcl the listener * @since 2.8 */ public final void removeFileChangeListener(FileChangeListener fcl) { getFCLSupport().removeFileChangeListener(fcl); } /** A hook for JAR FS */ void waitRefreshed() { } /** An action that it is to be called atomically with respect to filesystem event notification. * During its execution (via {@link FileSystem#runAtomicAction runAtomicAction}) * no events about changes in filesystems are fired. *

Nomenclature warning: the action is by no means "atomic" * in the usual sense of the word, i.e. either running to completion or rolling * back. There is no rollback support. The actual semantic property here is * close to "isolation" - the action appears as a single operation as far as * listeners are concerned - but not quite, since it is perfectly possible for * some other thread to see half of the action if it happens to run during * that time. Generally it is a mistake to assume that using AtomicAction gives * you any kind of consistency guarantees; rather, it avoids producing change * events too early and thus causing listener code to run before it should. */ public static interface AtomicAction { /** Executed when it is guaranteed that no events about changes * in filesystems will be notified. * * @exception IOException if there is an error during execution */ public void run() throws IOException; } static interface AsyncAtomicAction extends AtomicAction { boolean isAsynchronous(); } /** Class used to notify events for the filesystem. */ static abstract class EventDispatcher extends Object implements Runnable { public final void run() { dispatch(false, null); } /** @param onlyPriority if true then invokes only priority listeners * else all listeners are invoked. */ protected abstract void dispatch(boolean onlyPriority, Collection postNotify); /** @param propID */ protected abstract void setAtomicActionLink(EventControl.AtomicActionLink propID); } private static class FileStatusDispatcher extends EventDispatcher { private List listeners; private FileStatusEvent fStatusEvent; public FileStatusDispatcher(List listeners, FileStatusEvent fStatusEvent) { this.listeners = listeners; this.fStatusEvent = fStatusEvent; } protected void dispatch(boolean onlyPriority, Collection postNotify) { if (onlyPriority) { return; } for (FileStatusListener fStatusListener : listeners) { fStatusListener.annotationChanged(fStatusEvent); } } protected void setAtomicActionLink(EventControl.AtomicActionLink propID) { /** empty no fireFrom in FileStatusEvent*/ } } @SuppressWarnings("FieldMayBeFinal") private static StatusDecorator SFS_STATUS = new StatusDecorator() { @Override public String annotateNameHtml(String name, Set files) { // no HTML annotation return null; } @Override public String annotateName(String s, Set files) { // Look for a localized file name. // Note: all files in the set are checked. But please only place the attribute // on the primary file, and use this primary file name as the bundle key. for (FileObject fo : files) { // annotate a name String displayName = annotateName(fo); if (displayName != null) { return displayName; } } return s; } private final String annotateName(FileObject fo) { String bundleName = (String) fo.getAttribute("SystemFileSystem.localizingBundle"); // NOI18N if (bundleName != null) { try { bundleName = BaseUtilities.translate(bundleName); ResourceBundle b = NbBundle.getBundle(bundleName); try { return b.getString(fo.getPath()); } catch (MissingResourceException ex) { // ignore--normal } } catch (MissingResourceException ex) { Exceptions.attachMessage(ex, warningMessage(bundleName, fo)); LOG.log(Level.INFO, null, ex); // ignore } } return (String) fo.getAttribute("displayName"); // NOI18N } private String warningMessage(String name, FileObject fo) { Object by = fo.getAttribute("layers"); // NOI18N if (by instanceof Object[]) { by = Arrays.toString((Object[]) by); } return "Cannot load " + name + " for " + fo + " defined by " + by; // NOI18N } /* public Image annotateIcon(Image im, int type, Set files) { for (FileObject fo : files) { Image img = annotateIcon(fo, type); if (img != null) { return img; } } return im; } private Image annotateIcon(FileObject fo, int type) { String attr = null; if (type == BeanInfo.ICON_COLOR_16x16) { attr = "SystemFileSystem.icon"; // NOI18N } else if (type == BeanInfo.ICON_COLOR_32x32) { attr = "SystemFileSystem.icon32"; // NOI18N } if (attr != null) { Object value = fo.getAttribute(attr); if (value != null) { if (value instanceof URL) { return Toolkit.getDefaultToolkit().getImage((URL) value); } else if (value instanceof Image) { // #18832 return (Image) value; } else { LOG.warning("Attribute " + attr + " on " + fo + " expected to be a URL or Image; was: " + value); } } } String base = (String) fo.getAttribute("iconBase"); // NOI18N if (base != null) { if (type == BeanInfo.ICON_COLOR_16x16) { return ImageUtilities.loadImage(base, true); } else if (type == BeanInfo.ICON_COLOR_32x32) { return ImageUtilities.loadImage(insertBeforeSuffix(base, "_32"), true); // NOI18N } } return null; } */ }; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy