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

org.netbeans.modules.maven.cos.CopyResourcesOnSave Maven / Gradle / Ivy

The newest version!
/*
 * 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.netbeans.modules.maven.cos;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.model.Resource;
import org.codehaus.plexus.util.DirectoryScanner;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.java.api.common.project.BaseActionProvider;
import org.netbeans.modules.maven.api.FileUtilities;
import org.netbeans.modules.maven.api.NbMavenProject;
import org.netbeans.modules.maven.api.execute.RunUtils;
import org.netbeans.modules.maven.spi.cos.AdditionalDestination;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileRenameEvent;
import org.openide.filesystems.FileUtil;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;

/**
 * @author mkleint
 */
public class CopyResourcesOnSave extends FileChangeAdapter {

    private static final RequestProcessor RP = new RequestProcessor(CopyResourcesOnSave.class);
    private static final Logger LOG = Logger.getLogger(CopyResourcesOnSave.class.getName());
    
    private final NbMavenProject nbproject;
    private final Set resourceUris = new HashSet();
    private final Project project;
    private final PropertyChangeListener pchl = new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent pce) {
            if (NbMavenProject.PROP_PROJECT.equals(pce.getPropertyName())) {
                refresh();
            }
        }
    };

    public CopyResourcesOnSave(NbMavenProject nbprj, Project prj) {
        this.nbproject = nbprj;
        this.project = prj;
    }
    
    public final void opened() {
        refresh();
        nbproject.addPropertyChangeListener(pchl);
    }
    

    public final void closed() {
        nbproject.removePropertyChangeListener(pchl);
        synchronized (resourceUris) {
            for (File fl : resourceUris) {
                FileUtil.removeRecursiveListener(this, fl);
            }
            resourceUris.clear();
        }
    }
    
    final void refresh() {
        synchronized (resourceUris) {
            List resources = new ArrayList();
            resources.addAll(nbproject.getMavenProject().getResources());
            resources.addAll(nbproject.getMavenProject().getTestResources());
            Set old = new HashSet(resourceUris);
            Set added = new HashSet();
            
                for (Resource res : resources) {
                    String dir = res.getDirectory();
                    if (dir == null) {
                        continue;
                    }
                    URI uri = FileUtilities.getDirURI(project.getProjectDirectory(), dir);
                    File file = Utilities.toFile(uri);
                    if (!old.contains(file) && !added.contains(file)) { // if a given file is there multiple times, we get assertion back from FileUtil. there can be only one listener+file tuple
                        FileUtil.addRecursiveListener(this, file);
                    }
                    added.add(file);
                }
            
            old.removeAll(added);
            for (File oldFile : old) {
                FileUtil.removeRecursiveListener(this, oldFile);
            }
            resourceUris.removeAll(old);
            resourceUris.addAll(added);
        }
    }

    private static void copySrcToDest( FileObject srcFile, FileObject destFile) throws IOException {
        if (destFile != null && !srcFile.isFolder()) {
            InputStream is = null;
            OutputStream os = null;
            FileLock fl = null;
            try {
                is = srcFile.getInputStream();
                fl = destFile.lock();
                os = destFile.getOutputStream(fl);
                FileUtil.copy(is, os);
            } finally {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
                if (fl != null) {
                    fl.releaseLock();
                }
            }
        }
    }

    private Project getOwningMavenProject(FileObject file) {
        Project prj = FileOwnerQuery.getOwner(file);
        if (prj == null || !prj.equals(project)) {
            return null;
        }
        //#180447
        if (!prj.getProjectDirectory().isValid()) {
            return null;
        }
        NbMavenProject mvn = prj.getLookup().lookup(NbMavenProject.class);
        if (mvn == null) {
            return null;
        }
        if (RunUtils.isCompileOnSaveEnabled(prj)) {
            return prj;
        }
        return null;
    }

    /** Fired when a file is changed.
     * @param fe the event describing context where action has taken place
     */
    @Override
    public void fileChanged(final FileEvent fe) {
        RP.post(new Runnable() {//#167740
            @Override
            public void run() {
                fileChangedImpl(fe);
            }
        });
    }
    
    private  void fileChangedImpl(final FileEvent fe) {
        Project owning = getOwningMavenProject(fe.getFile());
        if (owning == null) {
            return;
        }
        try {
            handleCopyFileToDestDir(fe.getFile(), owning);
        } catch (IOException ex) {
            LOG.log(Level.INFO, null, ex);
        }
    }

    @Override
    public void fileDataCreated(final FileEvent fe) {
        RP.post(new Runnable() {//#167740
            @Override
            public void run() {
                fileDataCreatedImpl(fe);
            }
        });
    }
    private void fileDataCreatedImpl(final FileEvent fe) {
        Project owning = getOwningMavenProject(fe.getFile());
        if (owning == null) {
            return;
        }
        try {
            handleCopyFileToDestDir(fe.getFile(), owning);
        } catch (IOException ex) {
            LOG.log(Level.INFO, null, ex);
        }
    }

    @Override
    public void fileRenamed(final FileRenameEvent fe) {
        RP.post(new Runnable() {//#167740
            @Override
            public void run() {
                fileRenamedImpl(fe);
            }
        });
    }
    private void fileRenamedImpl(final FileRenameEvent fe) {
        try {
            FileObject fo = fe.getFile();
            Project owning = getOwningMavenProject(fo);
            if (owning == null) {
                return;
            }
            Tuple base = findAppropriateResourceRoots(fo, owning);
            if (base != null) {
                handleCopyFileToDestDir(base, fo, owning);
                FileObject parent = fo.getParent();
                String path;
                if (FileUtil.isParentOf(base.root, parent)) {
                    path = FileUtil.getRelativePath(base.root, fo.getParent()) +
                            "/" + fe.getName() + "." + fe.getExt(); //NOI18N
                } else {
                    path = fe.getName() + "." + fe.getExt(); //NOI18N
                }
                handleDeleteFileInDestDir(fo, path, base, owning);
            }
        } catch (IOException e) {
            LOG.log(Level.INFO, null, e);
        }
    }

    @Override
    public void fileDeleted(final FileEvent fe) {
        RP.post(new Runnable() {//#167740
            @Override
            public void run() {
                fileDeletedImpl(fe);
            }
        });
    }
    
    private void fileDeletedImpl(final FileEvent fe) {
        Project owning = getOwningMavenProject(fe.getFile());
        if (owning == null) {
            return;
        }
        try {
            handleDeleteFileInDestDir(fe.getFile(), null, owning);
        } catch (IOException ex) {
            LOG.log(Level.INFO, null, ex);
        }
    }

    private void handleDeleteFileInDestDir(FileObject fo, String path, Project project) throws IOException {
        Tuple tuple = findAppropriateResourceRoots(fo, project);
        handleDeleteFileInDestDir(fo, path, tuple, project);
    }

    private void handleDeleteFileInDestDir(FileObject fo, String path, Tuple tuple, Project project) throws IOException {
        if (tuple != null) {
            // inside docbase
            path = path != null ? path : FileUtil.getRelativePath(tuple.root, fo);
            path = addTargetPath(path, tuple.resource);
            FileObject toDelete = tuple.destinationRoot.getFileObject(path);
            if (toDelete != null) {
                toDelete.delete();
            }
            AdditionalDestination add = project.getLookup().lookup(AdditionalDestination.class);
            if (add != null) {
                add.delete(fo, path);
            }
        }
    }

    /** Copies a content file to an appropriate  destination directory, 
     * if applicable and relevant.
     */
    private void handleCopyFileToDestDir(FileObject fo, Project prj) throws IOException {
        Tuple tuple = findAppropriateResourceRoots(fo, prj);
        handleCopyFileToDestDir(tuple, fo, prj);
    }
    
    /** Copies a content file to an appropriate  destination directory,
     * if applicable and relevant.
     */
    private void handleCopyFileToDestDir(Tuple tuple, FileObject fo, Project project) throws IOException {
        if (tuple != null) {
            //TODO what to do with filtering? for now ignore..
            String path = FileUtil.getRelativePath(tuple.root, fo);
            path = addTargetPath(path, tuple.resource);
            createAndCopy(fo, tuple.destinationRoot, path);
                AdditionalDestination add = project.getLookup().lookup(AdditionalDestination.class);
            if (add != null) {
                add.copy(fo, path);
            }
        }
    }

    private static /* #172620 */synchronized void createAndCopy(FileObject fo, FileObject root, String path) throws IOException {
        copySrcToDest(fo, ensureDestinationFileExists(root, path, fo.isFolder()));
    }

    private String addTargetPath(String path, Resource resource) {
        String target = resource.getTargetPath();
        if (target != null) {
            target = target.replace("\\", "/");
            target = target.endsWith("/") ? target : (target + "/");
            path = target + path;
        }
        return path;
    }

    private Tuple findAppropriateResourceRoots(FileObject child, Project prj) {
        NbMavenProject nbproj = prj.getLookup().lookup(NbMavenProject.class);
        assert nbproj != null;
        if (RunUtils.isCompileOnSaveEnabled(prj)) {
            Tuple tup = findResource(nbproj.getMavenProject().getTestResources(), prj, nbproj, child, true);
            if (tup != null) {
                return tup;
            }
            tup = findResource(nbproj.getMavenProject().getResources(), prj, nbproj, child, false);
            if (tup != null) {
                return tup;
            }
        }
        return null;
    }

    private Tuple findResource(List resources, Project prj, NbMavenProject nbproj, FileObject child, boolean test) {
        LOG.log(Level.FINE, "findResource for {0}", child.getPath());        
        if (resources == null) {
            LOG.log(Level.FINE, "findResource for {0} : No Resources", child.getPath());
            return null;
        }
        FileObject target;
        //now figure the destination output folder
        File fil = nbproj.getOutputDirectory(test);
        File stamp = new File(fil, BaseActionProvider.AUTOMATIC_BUILD_TAG);
        if (stamp.exists()) {
            target = FileUtil.toFileObject(fil);
        } else {
            LOG.log(Level.FINE, "findResource for {0} : No Stamp", child.getPath());
            // no compile on save stamp, means no copying, classes don't get copied/compiled either.
            return null;
        }
        
        logResources(child, resources);

        resourceLoop:
        for (Resource res : resources) {
            String dir = res.getDirectory();
            if (dir == null) {
                continue;
            }
            URI uri = FileUtilities.getDirURI(prj.getProjectDirectory(), dir);
            FileObject fo = FileUtil.toFileObject(Utilities.toFile(uri));
            if (fo != null && FileUtil.isParentOf(fo, child)) {
                String path = FileUtil.getRelativePath(fo, child);
                //now check includes and excludes
                List incls = res.getIncludes();
                if (incls.isEmpty()) {
                    incls = Arrays.asList(FilteredResourcesCoSSkipper.DEFAULT_INCLUDES);
                }
                boolean included = false;
                for (String incl : incls) {
                    if (DirectoryScanner.match(incl, path)) {
                        included = true;
                        break;
                    }
                }
                if (!included) {
                    LOG.log(Level.FINE, "findResource for {0} : Not included {1}, {2} ", new Object[] {child.getPath(), included, res});
                    if(res.isFiltering()) {
                        continue;
                    } else {
                        break; 
                    }
                }
                List excls = new ArrayList(res.getExcludes());
                excls.addAll(Arrays.asList(DirectoryScanner.DEFAULTEXCLUDES));
                for (String excl : excls) {
                    if (DirectoryScanner.match(excl, path)) {
                        LOG.log(Level.FINER, "findResource for {0} : Excluded {1}, {2} ", new Object[] {child.getPath(), included, res});
                        continue resourceLoop;
                    }
                }
                LOG.log(Level.FINE, "findResource for {0} : Returns {1}, {2}, {3} ", new Object[] {child.getPath(), res, fo.getPath(), target});
                return new Tuple(res, fo, target);
            } else {
                LOG.log(Level.FINE, "findResource {0} does not apply to file {1}", new Object[]{res, child.getPath()});
            }
        }
        LOG.log(Level.FINE, "findResource for {0} : Retuerns Null", child.getPath());
        return null;
    }

    protected void logResources(FileObject fo, List resources) {
        if(LOG.isLoggable(Level.FINE)) {
            for (Resource res : resources) {
                LOG.log(Level.FINE, " {0}", res);
            }
        }
    }

    /** Returns the destination file or folder
     */
    private static FileObject ensureDestinationFileExists(FileObject root, String path, boolean isFolder) throws IOException {
        if (isFolder) {
            return FileUtil.createFolder(root, path);
        } else {
            return FileUtil.createData(root, path);
        }
    }

    private class Tuple {
        Resource resource;
        FileObject root;
        FileObject destinationRoot;

        private Tuple(Resource res, FileObject fo, FileObject destFolder) {
            resource = res;
            root = fo;
            destinationRoot = destFolder;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy