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.netbeans.modules.vcscore;

import java.beans.FeatureDescriptor;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.IOException;
import java.util.*;

import org.openide.filesystems.AbstractFileSystem;
import org.openide.filesystems.DefaultAttributes;
import org.openide.filesystems.FileObject;
import org.openide.util.RequestProcessor;
import org.openide.loaders.DataObject;

import org.netbeans.modules.masterfs.providers.Attributes;

import org.netbeans.api.vcs.commands.Command;
import org.netbeans.api.vcs.commands.CommandTask;

import org.netbeans.spi.vcs.VcsCommandsProvider;
import org.netbeans.spi.vcs.commands.CommandSupport;

import org.netbeans.modules.vcscore.cache.CacheReference;
import org.netbeans.modules.vcscore.caching.FileStatusProvider;
import org.netbeans.modules.vcscore.caching.FileCacheProvider;
import org.netbeans.modules.vcscore.actions.CommandActionSupporter;
import org.netbeans.modules.vcscore.actions.GeneralCommandAction;
import org.netbeans.modules.vcscore.commands.VcsCommand;
//import org.netbeans.modules.vcscore.commands.VcsCommandExecutor;
import org.netbeans.modules.vcscore.commands.VcsDescribedCommand;
import org.netbeans.modules.vcscore.runtime.RuntimeCommandsProvider;
import org.netbeans.modules.vcscore.search.VcsSearchTypeFileSystem;
import org.netbeans.modules.vcscore.util.Table;
import org.netbeans.modules.vcscore.util.virtuals.VirtualsDataLoader;

/**
 * Implementation of file attributes for version control systems. All attributes
 * read/write operations are delegated to the DefaultAttributes, whith the exception
 * of VCS-related attributes. These special attributes are not propagated to
 * the DefaultAttributes, but are interpreted as VCS commands.
 * @author  Martin Entlicher
 */
public class VcsAttributes extends Attributes {
    
    /**
     * The name of attribute, that contains the java.io.File object for
     * the given FileObject
     */
    public static final String FILE_ATTRIBUTE = "java.io.File"; //NOI18N

    /**
     * Name of the attribute that contains the instance of the vcs filesystem that the
     * fileobject belongs to.
     */
    public static final String VCS_NATIVE_FS = "VcsFileSystemAttributeIdentifier";

    /**
     * Name of the attribute that contains path of the fileobject within the versioning filesystem.
     * That can differ when the filesystem is placed within a multifilesystem.
     * In cooperation with the VCS_NATIVE_FS one can retrieve the original fileobject from
     * the vcs filesystem.
     */
    public static final String VCS_NATIVE_PACKAGE_NAME_EXT = "VcsFileSystemNativeFOPath";
    
    /**
     * The name of FileObject attribute, that contains instance of VcsCommandsProvider
     * on VCS filesystems.
     */
    private static final String VCS_COMMANDS_PROVIDER_ATTRIBUTE = "org.netbeans.spi.vcs.VcsCommandsProvider"; // NOI18N

    public static final String RUNTIME_PROVIDER = "org.netbeans.modules.vcscore.runtime.RuntimeCommandsProvider"; // NOI18N
    
    /**
     * Attribute name for a VCS action.
     */
    public static final String VCS_ACTION = "VCS_ACTION"; //NOI18N
    /**
     * Attribute name for the refresh action on a VCS filesystem.
     */
    public static final String VCS_REFRESH = "VCS_REFRESH"; //NOI18N
    /**
     * Attribute name for a VCS action, that schedules the VCS operation for later processing.
     * This action should be performed on important secondary file objects.
     */
    public static final String VCS_SCHEDULING_SECONDARY_FO_ACTION = "VCS_SCHEDULING_SECONDARY_FO_ACTION"; //NOI18N
    /**
     * The scheduling VCS Add action name. The file will be added to the VCS repository
     * as soon as the primary file will be committed.
     */
    public static final String VCS_SCHEDULING_ADD = "ADD"; //NOI18N
    /**
     * The scheduling VCS Remove action name. The file will be removed from the VCS repository
     * as soon as the primary file will be committed.
     */
    public static final String VCS_SCHEDULING_REMOVE = "REMOVE"; //NOI18N
    /**
     * The attribute name where scheduled files are stored.
     */
    public static final String VCS_SCHEDULED_FILES_ATTR = "VCS_SCHEDULED_FILES"; //NOI18N
    /**
     * The attribute name marking the file as scheduled for later processing.
     * The value should be {@link VCS_SCHEDULING_ADD} or {@link VCS_SCHEDULING_REMOVE}.
     */
    public static final String VCS_SCHEDULED_FILE_ATTR = "VCS_SCHEDULED_FILE"; //NOI18N
    /**
     * The attribute name containing the java.io.File name of the primary file,
     * that contains scheduled files. This will prevent the copy of scheduled
     * attributes to other files, because when the value of this attribue will
     * differ from the actual file path, all scheduled attribues will be deleted.
     */
    public static final String VCS_SCHEDULING_MASTER_FILE_NAME_ATTR = "VCS_SCHEDULING_MASTER_FILE_NAME"; //NOI18N
    /**
     * The VCS Add command name. This command adds the file to the VCS repository.
     */
    public static final String VCS_ACTION_ADD = "VCS_ADD"; //NOI18N
    /**
     * The VCS Remove command name. This command removes the file from the VCS repository.
     */
    public static final String VCS_ACTION_REMOVE = "VCS_REMOVE"; //NOI18N
    /**
     * This attribute is set when the action is done. The value is Boolean.TRUE
     * or Boolean.FALSE depending on the command exit status.
     */
    public static final String VCS_ACTION_DONE = "VCS_ACTION_DONE"; //NOI18N
    /**
     * The description to the action. Usually it is a message, that is given to
     * the VCS command (such as a change log for check in)
     */
    public static final String VCS_ACTION_DESCRIPTION = "VCS_ACTION_DESCRIPTION"; //NOI18N
    
    /**
     * Read the attribute of this name to obtain the VCS file status.
     */
    public static String VCS_STATUS = "VCS_STATUS"; //NOI18N
    /**
     * The status, that is returned from {@link readAttribute} for files,
     * that are not version controlled.
     */
    public static String VCS_STATUS_LOCAL = "VCS_STATUS_LOCAL"; //NOI18N
    /**
     * The status, that is returned from {@link readAttribute} for files,
     * that are present in VCS repository, but do not exist locally.
     */
    public static String VCS_STATUS_MISSING = "VCS_STATUS_MISSING"; //NOI18N
    /**
     * The status, that is returned from {@link readAttribute} for files,
     * that are version controlled and are present locally.
     */
    public static String VCS_STATUS_UP_TO_DATE = "VCS_STATUS_UP_TO_DATE"; //NOI18N
    /**
     * The status, that is returned from {@link readAttribute} for files,
     * that are not recognized. We can not say whether they are version controlled
     * or not.
     */
    public static String VCS_STATUS_UNKNOWN = "VCS_STATUS_UNKNOWN"; //NOI18N
    
    private transient VcsActionSupporter supporter;
    
    private transient VcsCommandsProvider commandsProvider;
    
    private transient RuntimeCommandsProvider runtimeProvider;
        
    private VcsFileSystem fileSystem;
    
    //private static RequestProcessor vcsActionRequestProcessor;

    static final long serialVersionUID = 8084585278800267078L;
    
    /** Creates new VcsAttributes */
    public VcsAttributes(File mountPoint, AbstractFileSystem.Info info, AbstractFileSystem.Change change,
                         AbstractFileSystem.List list, VcsFileSystem fileSystem, VcsActionSupporter supp) {
        super(mountPoint, info, change, list);
        this.fileSystem = fileSystem;
        supporter = supp;
        commandsProvider = fileSystem.getCommandsProvider();
    }
    
    public VcsActionSupporter getCurrentSupporter() {
        return supporter;
    }
    
    public void setCurrentSupporter(VcsActionSupporter supporter) {
        this.supporter = supporter;
    }
    
    public VcsCommandsProvider getCommandsProvider() {
        return commandsProvider;
    }
    
    public void setCommandsProvider(VcsCommandsProvider commandsProvider) {
        this.commandsProvider = commandsProvider;
    }
    
    public RuntimeCommandsProvider getRuntimeCommandsProvider() {
        return runtimeProvider;
    }
    
    public void setRuntimeCommandsProvider(RuntimeCommandsProvider provider) {
        this.runtimeProvider = provider;
    }
    
    /**
     * Get the file attribute with the specified name.
     * @param name the file name
     * @param attrName name of the attribute
     * @return appropriate (serializable) value or null if the attribute is unset
     *         (or could not be properly restored for some reason).
     *         If the attribute name is the {@link VCS_STATUS}, then the VCS status
     *         of the file is returned.
     */
    public Object readAttribute(String name, String attrName) {
        if (FILE_ATTRIBUTE.equals(attrName)) {
            java.io.File file = fileSystem.getFile(name);
            if (!file.isAbsolute()) {
                // It's dangerous to return a file, that is not absolute.
                // E.g. loaders are broken after a non-absolute file is returned.
                // See issue #32698 for details.
                return null;
            } else {
                return file;
            }
        }
        if (RUNTIME_PROVIDER.equals(attrName)) {
            return runtimeProvider;
        }
        if (VCS_STATUS.equals(attrName)) {
            if (!fileSystem.getFile(name).exists()) return VCS_STATUS_MISSING;

            FileStatusProvider statusProvider = fileSystem.getStatusProvider();
            if (statusProvider != null) {
                String status = statusProvider.getFileStatus(name);
                //CacheFile file = cacheProvider.getFile(name);
                if (statusProvider.getLocalFileStatus().equals(status)) {
                    return VCS_STATUS_LOCAL;
                }
                return VCS_STATUS_UP_TO_DATE;
            }
            return VCS_STATUS_UNKNOWN;
        } else if (GeneralCommandAction.VCS_ACTION_ATTRIBUTE.equals(attrName)) {
            return supporter;            
        } else if (VCS_NATIVE_FS.equals(attrName)) {
            return fileSystem;
        } else if (VCS_NATIVE_PACKAGE_NAME_EXT.equals(attrName)) {
            return name;
        } else if (VCS_COMMANDS_PROVIDER_ATTRIBUTE.equals(attrName)) {
            return commandsProvider;
        } else if (VcsSearchTypeFileSystem.VCS_SEARCH_TYPE_ATTRIBUTE.equals(attrName)) {
            return fileSystem;
        }  else {

            if ("NetBeansAttrAssignedLoader".equals(attrName)) { /* DataObject.EA_ASSIGNED_LOADER */  //NOI18N
                CacheReference ref = fileSystem.getCacheReference(name);
                if ( (ref != null) && ref.isVirtual()) {
                        return VirtualsDataLoader.class.getName();
                }
            } else if ("NetBeansAttrAssignedLoaderModule".equals(attrName)) { /* DataObject.EA_ASSIGNED_LOADER_MODULE */  //NOI18N
                CacheReference ref = fileSystem.getCacheReference(name);
                if (ref != null) {
                    if (ref.isVirtual()) {
                        //                        System.out.println("is vitrual module..");
                        return "org.netbeans.modules.vcscore"; //NOI18N
                    }
                }
            }
            return super.readAttribute(name, attrName);
        }
    }

    /**
     * Set the file attribute with the specified name. If the name is {@link VCS_ACTION},
     * and the value is an instance of FeatureDescriptor, then it's not set as file attribute,
     * but is interpreted as a VCS command. The name of the command is taken from
     * value.getName() and commands options from attributes of that feature descriptor.
     * @param name the file name
     * @param attrName name of the attribute
     * @param value new value or null to clear the attribute. Must be serializable,
     *        with the exception of VCS command attribute.
     * @throws IOException if the attribute cannot be set. If serialization is
     *                     used to store it, this may in fact be a subclass such
     *                     as NotSerializableException.
     * @throws java.net.UnknownServiceException if the requested VCS action is not provided.
     *                                 A subclass of IOException was chosen, since
     *                                 FileObject.setAttribute throws IOException.
     */
    public void writeAttribute(final String name, final String attrName, final Object value) throws IOException, java.net.UnknownServiceException {
        if (VCS_ACTION.equals(attrName) && value instanceof FeatureDescriptor) {
            performVcsAction(name, (FeatureDescriptor) value);
        } else if (VCS_REFRESH.equals(attrName)) {
            performRefresh(name, value);
        } else if (VCS_SCHEDULING_SECONDARY_FO_ACTION.equals(attrName) && value instanceof String) {
            // Set the scheduling action for a secondary file. value is the action name,
            // currently "ADD" and "REMOVE" are the only supported values
            final FileObject fo = fileSystem.findFileObject(name);
            //System.out.println("scheduleSecondaryFOVcsAction("+name+", "+actionName+") = "+fo);
            if (fo == null) return ;
            FileObject primary;
            try {
                org.openide.loaders.DataObject dobj = org.openide.loaders.DataObject.find(fo);
                primary = dobj.getPrimaryFile();
                //System.out.println("  primary("+primary+").equals("+fo+") = "+primary.equals(fo));
                if (primary.equals(fo)) return ;
            } catch (org.openide.loaders.DataObjectNotFoundException exc) {
                exc.printStackTrace();
                return ;
            }
            // The scheduling is started. When all scheduling actions are done, a refresh is introduced.
            startFileScheduling(name);
            final FileObject primaryFO = primary;
            RequestProcessor.getDefault().post(new Runnable() {
                public void run() {
                    scheduleSecondaryFOVcsAction(name, (String) value, fo, primaryFO);
                }
            });
            super.writeAttribute(name, VCS_SCHEDULED_FILE_ATTR, value);
        } else {

            if ("NetBeansAttrAssignedLoader".equals(attrName)) { /* DataObject.EA_ASSIGNED_LOADER */  //NOI18N
                if (value == null) {
                    CacheReference ref = fileSystem.getCacheReference(name);
                    if (ref != null) {
                        ref.setVirtual(false);
                    }
                }
                else if (VirtualsDataLoader.class.getName().equals(value)) {
                    CacheReference ref = fileSystem.getCacheReference(name);
                    if (ref != null) {
                        ref.setVirtual(true);
                    }
                    return;
                }
            }    
            if ("NetBeansAttrAssignedLoaderModule".equals(attrName)) { /* DataObject.EA_ASSIGNED_LOADER_MODULE */  //NOI18N
                if (value != null && "org.netbeans.modules.vcscore".equals(value.toString())  //NOI18N
                    && fileSystem.checkVirtual(name)) {
                   //don't write to .nbattrs file..
                   return;  
                }
                //System.out.println("write assigned module=" + value);
            }                
            super.writeAttribute(name, attrName, value);
        }
    }
    
    /*
    private static synchronized RequestProcessor getVcsActionRequestProcessor() {
        if (vcsActionRequestProcessor == null) {
            vcsActionRequestProcessor = new RequestProcessor("Vcs Attribute Action Request Processor");
        }
        return vcsActionRequestProcessor;
    }
     */
    
    /**
     * Perform a VCS command on a specific file.
     * @param name the file the command should run on
     * @param descriptor the descriptor of the command. descriptor.getName() should
     *        return the command name, attributes can contain variable values
     *        which are given to the command. VCS_ACTION_DONE attribute is set
     *        when the action is done with the value being Boolean.TRUE or Boolean.FALSE
     *        depending on the command exit status.
     */
    private void performVcsAction(final String name, final FeatureDescriptor descriptor) throws java.net.UnknownServiceException {
        performVcsAction(fileSystem.findResource(name), descriptor);
    }
    
    /**
     * Perform a VCS command on a specific file.
     * @param fo the file object the command should run on
     * @param descriptor the descriptor of the command. descriptor.getName() should
     *        return the command name, attributes can contain variable values
     *        which are given to the command. VCS_ACTION_DONE attribute is set
     *        when the action is done with the value being Boolean.TRUE or Boolean.FALSE
     *        depending on the command exit status.
     */
    private void performVcsAction(final FileObject fo, final FeatureDescriptor descriptor) throws java.net.UnknownServiceException {
        //System.out.println("performVcsAction("+name+")");
        String cmdName = descriptor.getName();
        final CommandSupport cmdSupport = fileSystem.getCommandSupport(cmdName);
        if (cmdSupport == null) throw new java.net.UnknownServiceException(cmdName);
        final Command cmd = cmdSupport.createCommand();
        FileObject[] files = new FileObject[] { fo };
        cmd.setFiles(files);
        //final Table files = new Table();
        //files.put(name, fileSystem.findResource(name));
        final Hashtable additionalVars = new Hashtable();
        for (Enumeration varNames = descriptor.attributeNames(); varNames.hasMoreElements(); ) {
            String varName = (String) varNames.nextElement();
            additionalVars.put(varName, descriptor.getValue(varName));
        }
        if (cmd instanceof VcsDescribedCommand) {
            ((VcsDescribedCommand) cmd).setAdditionalVariables(additionalVars);
        }
        RequestProcessor.getDefault().post(new Runnable() {
            public void run() {
                CommandTask task = cmd.execute();
                task.waitFinished();
                boolean status = task.getExitStatus() == task.STATUS_SUCCEEDED;
                /*
                VcsCommandExecutor[] executors = VcsAction.doCommand(files, cmd, additionalVars, fileSystem);
                boolean status = true;
                for (int i = 0; i < executors.length; i++) {
                    try {
                        fileSystem.getCommandsPool().waitToFinish(executors[i]);
                    } catch (InterruptedException iexc) {
                        return ;
                    }
                    status &= executors[i].getExitStatus() == VcsCommandExecutor.SUCCEEDED;
                }
                 */
                descriptor.setValue(VCS_ACTION_DONE, Boolean.valueOf(status));
            }
        });
    }
    
    /**
     * Do a refresh of the folder.
     * @param name the folder name
     * @param recursive if it's value is Boolean.TRUE, a recursive refresh
     * of this folder is performed.
     */
    private void performRefresh(final String name, final Object recursive) {
        RequestProcessor.getDefault().post(new Runnable() {
            public void run() {
            boolean rec = Boolean.TRUE.equals(recursive);

            FileCacheProvider cache = fileSystem.getCacheProvider();
            if (cache != null) {
                if (rec) {
                    cache.refreshCacheDirRecursive(name);
                } else {
                    cache.refreshCacheDir(name);
                }
            }
            }
        });
    }
    
    /**
     * Schedule a secondary file for "ADD" or "REMOVE". Files scheduled for remove are filtered from children().
     * Files scheduled are added as a special file attribute to the primary file.
     * @param name the name of a secondary file to be scheduled.
     * @param actionName "ADD" or "REMOVE"
     * @param fo the file object associated to the file
     * @param primary the associated primary file
     */
    private boolean scheduleSecondaryFOVcsAction(final String name, final String actionName, FileObject fo, FileObject primary) {
        if (VCS_STATUS_LOCAL.equals(primary.getAttribute(VCS_STATUS))) {
            // do not schedule local files
            endFileScheduling(name);
            return false;
        }
        int id;
        // create the descriptor of a scheduling action
        FeatureDescriptor descriptor = new FeatureDescriptor() {
            public void setValue(String attrName, Object value) {
                if (VCS_ACTION_DONE.equals(attrName)) {
                    // the scheduling action is done, inform the file system
                    fileSystem.removeScheduledFileToBeProcessed(name);
                    endFileScheduling(name);
                }
                super.setValue(attrName, value);
            }
        };
        boolean endOfScheduling = false;
        if (VCS_SCHEDULING_ADD.equals(actionName)) {
            //fileSystem.addScheduledSecondaryFO(name, VcsFileSystem.SCHEDULING_ACTION_ADD_ID);
            //FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(VcsCommand.NAME_SCHEDULE_ADD);
            try {
                fileSystem.addScheduledFileToBeProcessed(name);
                performVcsAction(fo, descriptor);
            } catch (java.net.UnknownServiceException unsExc) {
                fileSystem.removeScheduledFileToBeProcessed(name);
                endOfScheduling = true;
            }
            id = 1;
        } else if (VCS_SCHEDULING_REMOVE.equals(actionName)) {
            //fileSystem.addScheduledSecondaryFO(name, VcsFileSystem.SCHEDULING_ACTION_REMOVE_ID);
            //FeatureDescriptor descriptor = new FeatureDescriptor();
            descriptor.setName(VcsCommand.NAME_SCHEDULE_REMOVE);
            try {
                fileSystem.addScheduledFileToBeProcessed(name);
                performVcsAction(fo, descriptor);
            } catch (java.net.UnknownServiceException unsExc) {
                fileSystem.removeScheduledFileToBeProcessed(name);
                endOfScheduling = true;
            }
            id = 0;
        } else {
            endFileScheduling(name);
            return false;
        }
        // the file is being scheduled, add it to the primary file attribute.
        Set[] scheduled = (Set[]) primary.getAttribute(VCS_SCHEDULED_FILES_ATTR);
        if (scheduled == null) scheduled = new HashSet[2];
        if (scheduled[id] == null) scheduled[id] = new HashSet();
        scheduled[id].add(name);
        try {
            primary.setAttribute(VCS_SCHEDULED_FILES_ATTR, scheduled);
            java.io.File file = org.openide.filesystems.FileUtil.toFile(primary);
            if (file != null) {
                primary.setAttribute(VCS_SCHEDULING_MASTER_FILE_NAME_ATTR, file.getAbsolutePath());
            }
        } catch (IOException ioExc) {
            if (endOfScheduling) endFileScheduling(name);
            return false;
        }
        if (endOfScheduling) endFileScheduling(name);
        return true;
    }
    
    private transient Map schedulingFilesByFolders;
    
    /**
     * Remember the file as being scheduled for it's folder.
     */
    private void startFileScheduling(String name) {
        synchronized (this) {
            if (schedulingFilesByFolders == null) {
                schedulingFilesByFolders = new HashMap();
            }
            int index = name.lastIndexOf('/');
            String dir = (index < 0) ? "" : name.substring(0, index);
            String file = (index < 0) ? name : index < (name.length() - 1) ? name.substring(index + 1) : "";
            Set files = (Set) schedulingFilesByFolders.get(dir);
            if (files == null) {
                files = new HashSet();
            }
            files.add(file);
            schedulingFilesByFolders.put(dir, files);
        }
    }
    
    /**
     * The scheduling action for this file was done, if no more actions are being
     * processed in the file's folder, do a refresh of that folder.
     */
    private void endFileScheduling(String name) {
        Set files;
        String dir;
        synchronized (this) {
            if (schedulingFilesByFolders == null) {
                schedulingFilesByFolders = new HashMap();
            }
            int index = name.lastIndexOf('/');
            dir = (index < 0) ? "" : name.substring(0, index);
            String file = (index < 0) ? name : index < (name.length() - 1) ? name.substring(index + 1) : "";
            files = (Set) schedulingFilesByFolders.get(dir);
            if (files != null) {
                files.remove(file);
                if (files.size() == 0) files = null;
            }
            if (files == null) {
                schedulingFilesByFolders.remove(dir);
            }
        }
        if (files == null) {
            performRefresh(dir, Boolean.FALSE);
        }
    }
    
    private void readObject (java.io.ObjectInputStream ois)
        throws ClassNotFoundException, IOException {
        ois.defaultReadObject();
        /*
        if (supporter != null && supporter instanceof VcsActionSupporter) {
            ((VcsActionSupporter)supporter).setFileSystem(fileSystem);
        }
         */
    }


}
... 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.