org.eclipse.imp.releng.CopyrightAdder.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.imp.releng.CopyrightAdder.java

Source

/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
    
*******************************************************************************/

package org.eclipse.imp.releng;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.imp.releng.dialogs.SelectFeatureInfosDialog;
import org.eclipse.imp.releng.metadata.FeatureInfo;
import org.eclipse.imp.releng.metadata.PluginInfo;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ui.PlatformUI;

public class CopyrightAdder {
    private interface ICopyrightAdder {
        String addCopyright(String src);
    }

    private class JavaEPLCopyrightAdder implements ICopyrightAdder {
        private Pattern COPYRIGHT_PATTERN = Pattern
                .compile("/\\*.*\n" + "\\s*\\* Copyright \\(c\\) [0-9]+ IBM Corporation.\n"
                        + "\\s*\\* All rights reserved. This program and the accompanying materials\n"
                        + "\\s*\\* are made available under the terms of the Eclipse Public License v1.0\n"
                        + "\\s*\\* which accompanies this distribution, and is available at\n"
                        + "\\s*\\* http://www.eclipse.org/legal/epl-v10.html\n" + "\\s*\\*\n"
                        + "\\s*\\* Contributors:\n" + "(\\s*\\*.*\n)+" + // 1 or more contributor lines
                        "\\s*\\*?\n" + ".*\\*/\n");

        private static final String COPYRIGHT_NOTICE = "/*******************************************************************************\n"
                + "* Copyright (c) 2008 IBM Corporation.\n"
                + "* All rights reserved. This program and the accompanying materials\n"
                + "* are made available under the terms of the Eclipse Public License v1.0\n"
                + "* which accompanies this distribution, and is available at\n"
                + "* http://www.eclipse.org/legal/epl-v10.html\n" + "*\n" + "* Contributors:\n"
                + "*    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation\n" + "*\n"
                + "*******************************************************************************/\n\n";

        /**
         * This is the old, pre-eclipse.org copyright notice that needs to be replaced.
         */
        private static final String OLD_COPYRIGHT_NOTICE = "/*\r\n" + " * (C) Copyright IBM Corporation 2007\r\n"
                + " * \r\n" + " * This file is part of the Eclipse IMP.\r\n" + " */\r\n";

        private final int OLD_COPYRIGHT_LENGTH = OLD_COPYRIGHT_NOTICE.length();

        public String removeOldCopyright(String src) {
            int oldStart = src.indexOf(OLD_COPYRIGHT_NOTICE);
            if (oldStart < 0)
                return src;
            if (oldStart == 0)
                return src.substring(OLD_COPYRIGHT_LENGTH);
            String prefix = src.substring(0, oldStart);
            String suffix = src.substring(oldStart + OLD_COPYRIGHT_LENGTH);
            return prefix + suffix;
        }

        public String addCopyright(String src) {
            Matcher m = COPYRIGHT_PATTERN.matcher(src);
            if (m.find())
                return src; // already has the new copyright notice
            src = removeOldCopyright(src);
            return COPYRIGHT_NOTICE + src;
        }
    }

    private class LPGEPLCopyrightAdder implements ICopyrightAdder {
        private final Pattern COPYRIGHT_PATTERN = Pattern.compile("%Notice\n" + "/.\n"
                + "////////////////////////////////////////////////////////////////////////////////\n"
                + "// Copyright (c) [0-9]+ IBM Corporation.\n"
                + "// All rights reserved. This program and the accompanying materials\n"
                + "// are made available under the terms of the Eclipse Public License v1.0\n"
                + "// which accompanies this distribution, and is available at\n"
                + "// http://www.eclipse.org/legal/epl-v10.html\n" + "//\n" + "//Contributors:\n" + "(//.*\n)+" + // 1 or more contributor lines
                "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "./\n"
                + "%End\n" + "\n");

        private static final String COPYRIGHT_NOTICE = "%Notice\n" + "/.\n"
                + "////////////////////////////////////////////////////////////////////////////////\n"
                + "// Copyright (c) 2007 IBM Corporation.\n"
                + "// All rights reserved. This program and the accompanying materials\n"
                + "// are made available under the terms of the Eclipse Public License v1.0\n"
                + "// which accompanies this distribution, and is available at\n"
                + "// http://www.eclipse.org/legal/epl-v10.html\n" + "//\n" + "//Contributors:\n"
                + "//    Philippe Charles (pcharles@us.ibm.com) - initial API and implementation\n" + "\n"
                + "////////////////////////////////////////////////////////////////////////////////\n" + "./\n"
                + "%End\n" + "\n";

        private static final String OLD_COPYRIGHT_NOTICE = "%Notice\r\n" + "/.\r\n"
                + "// (C) Copyright IBM Corporation 2007\r\n" + "// \r\n"
                + "// This file is part of the Eclipse IMP.\r\n" + "./\r\n" + "%End\r\n" + "\r\n";

        private final int OLD_COPYRIGHT_LENGTH = OLD_COPYRIGHT_NOTICE.length();

        private final Pattern RULES_PATTERN = Pattern.compile("^\\s*%[Rr][Uu][Ll][Ee][Ss]");

        public String removeOldCopyright(String src) {
            int oldStart = src.indexOf(OLD_COPYRIGHT_NOTICE);
            if (oldStart < 0)
                return src;
            if (oldStart == 0)
                return src.substring(OLD_COPYRIGHT_LENGTH);
            String prefix = src.substring(0, oldStart);
            String suffix = src.substring(oldStart + OLD_COPYRIGHT_LENGTH);
            return prefix + suffix;
        }

        public String addCopyright(String src) {
            Matcher cm = COPYRIGHT_PATTERN.matcher(src);
            if (cm.find())
                return src;
            src = removeOldCopyright(src);
            Matcher rm = RULES_PATTERN.matcher(src);
            int rulesLoc = -1;
            if (rm.find()) {
                rulesLoc = rm.start(); // Look for the new keyword form ("%Rules") first; "XXX$Rules" is a legal right-hand side symbol reference
            }
            if (rulesLoc < 0) {
                rulesLoc = src.indexOf("$Rules");
            }
            if (rulesLoc < 0) {
                rulesLoc = 0;
            }
            return src.substring(0, rulesLoc) + COPYRIGHT_NOTICE + src.substring(rulesLoc);
        }
    }

    private ReleaseTool fReleaseTool = new WorkbenchReleaseTool();

    private Map<String, ICopyrightAdder> fExtensionMap = new HashMap<String, ICopyrightAdder>();

    {
        LPGEPLCopyrightAdder lpgAdder = new LPGEPLCopyrightAdder();

        fExtensionMap.put("g", lpgAdder);
        fExtensionMap.put("gi", lpgAdder);
        fExtensionMap.put("java", new JavaEPLCopyrightAdder());
    }

    private final Set<IFolder> fSrcRoots = new HashSet<IFolder>();
    private final List<IFile> fChangedFiles = new ArrayList<IFile>();
    private CompositeChange fTopChange;

    private int fModCount = 0;

    public void addCopyrightTo(IResource resource) {
        computeChangeForCopyrights(resource);
        ReleaseTool.doPerformChange(fTopChange, "Adding copyright notices", fChangedFiles);
        ReleaseEngineeringPlugin.getMsgStream().println("Modified " + fModCount + " files.");
    }

    public CompositeChange computeChangeForCopyrights(IResource resource) {
        fChangedFiles.clear();
        fTopChange = new CompositeChange("Copyright additions");

        if (resource instanceof IFile) {
            processFile((IFile) resource, fTopChange);
        } else if (resource instanceof IFolder) {
            fSrcRoots.clear();
            fSrcRoots.add((IFolder) resource);
            traverseSrcRootsAddCopyrights(fTopChange);
        }
        return fTopChange;
    }

    public void addCopyrights() {
        //      final IPreferenceStore prefStore= ReleaseEngineeringPlugin.getInstance().getPreferenceStore();

        fReleaseTool.collectMetaData(true);

        fChangedFiles.clear();
        fTopChange = new CompositeChange("Copyright additions");
        fSrcRoots.clear();

        List<FeatureInfo> featureInfos = fReleaseTool.getFeatureInfos();

        final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
        SelectFeatureInfosDialog sfid = new SelectFeatureInfosDialog(shell, featureInfos);

        if (sfid.open() != Dialog.OK) {
            return;
        }

        IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
        Set<IProject> projects = new HashSet<IProject>();

        for (FeatureInfo featureInfo : sfid.getSelectedFeatures()) {
            for (PluginInfo plugin : featureInfo.fPluginInfos) {
                projects.add(wsRoot.getProject(plugin.fProjectName));
            }
        }

        collectProjectSourceRoots(projects);

        fModCount = 0;
        traverseSrcRootsAddCopyrights(fTopChange);

        if (fTopChange.getChildren().length == 0) {
            MessageDialog.openInformation(shell, "No changes needed", "No source files lack a copyright notice.");
            return;
        }

        Dialog d = new ConfirmChangedFilesDialog(shell, fTopChange);

        if (d.open() == Dialog.OK) {
            ReleaseTool.doPerformChange(fTopChange, "Adding copyright notices", fChangedFiles);
            ReleaseEngineeringPlugin.getMsgStream().println("Modified " + fModCount + " files.");
        }
    }

    private void traverseSrcRootsAddCopyrights(final CompositeChange topChange) {
        final Map<IResource, CompositeChange> resource2Change = new HashMap<IResource, CompositeChange>();
        for (IFolder srcRoot : fSrcRoots) {
            IProject project = srcRoot.getProject();

            if (!resource2Change.containsKey(project)) {
                CompositeChange projChange = new CompositeChange("Project " + project.getName());
                topChange.add(projChange);
                resource2Change.put(project, projChange);
            }
            CompositeChange folderChange = new CompositeChange(/*"Adding notices to folder " + */srcRoot.getName());
            resource2Change.put(srcRoot, folderChange);
            try {
                srcRoot.accept(new IResourceVisitor() {
                    public boolean visit(IResource resource) throws CoreException {
                        if (resource.isDerived())
                            return false;

                        if (resource instanceof IFile) {
                            processFile((IFile) resource, resource2Change.get(resource.getParent()));
                        } else if (resource instanceof IFolder) {
                            ReleaseEngineeringPlugin.getMsgStream()
                                    .println("  Scanning folder " + resource.getLocation().toPortableString());
                            CompositeChange newComp = new CompositeChange(
                                    /*"Adding notices to folder " + */resource.getName());
                            resource2Change.put(resource, newComp);
                            resource2Change.get(resource.getParent()).add(newComp);
                        }
                        return true;
                    }
                });
            } catch (CoreException e) {
                logError(e);
                e.printStackTrace();
            }
        }
        pruneEmptyChanges(fTopChange);
    }

    private void pruneEmptyChanges(Change change) {
        if (change instanceof CompositeChange) {
            CompositeChange compositeChange = (CompositeChange) change;
            for (Change child : compositeChange.getChildren()) {
                pruneEmptyChanges(child);
            }
            if (compositeChange.getChildren().length == 0) {
                ((CompositeChange) compositeChange.getParent()).remove(compositeChange);
            }
        }
    }

    private void processFile(IFile file, CompositeChange parentChange) {
        String fileExtension = file.getFileExtension();

        if (fExtensionMap.containsKey(fileExtension)) {
            ICopyrightAdder copyrightAdder = fExtensionMap.get(fileExtension);

            addCopyright(copyrightAdder, file, parentChange);
        }
    }

    private void addCopyright(ICopyrightAdder copyrightAdder, IFile file, CompositeChange parentChange) {
        try {
            //          ReleaseEngineeringPlugin.getMsgStream().println("    Processing file " + file.getLocation().toPortableString());
            InputStream is = file.getContents();
            String origCont = getFileContents(new InputStreamReader(is));

            final String newCont = copyrightAdder.addCopyright(origCont);

            if (newCont.equals(origCont)) {
                return;
            }

            ReleaseEngineeringPlugin.getMsgStream()
                    .println("        File " + file.getName() + " needs a copyright notice.");
            fModCount++;

            TextFileChange tfc = new TextFileChange("Add copyright to " + file.getName(), file);
            long curFileLength = new File(file.getLocation().toOSString()).length();

            tfc.setEdit(new MultiTextEdit());
            tfc.addEdit(new ReplaceEdit(0, (int) curFileLength, newCont));

            fChangedFiles.add(file);
            parentChange.add(tfc);
        } catch (CoreException e) {
            logError(e);
            e.printStackTrace();
        }
    }

    /**
     * @param projects
     */
    private void collectProjectSourceRoots(Set<IProject> projectsToModify) {
        IWorkspaceRoot wsRoot = fReleaseTool.fWSRoot;

        for (IProject project : projectsToModify) {
            ReleaseEngineeringPlugin.getMsgStream().println("Collecting source folders for " + project.getName());

            IJavaProject javaProject = JavaCore.create(project);

            if (!javaProject.exists()) {
                ReleaseEngineeringPlugin.getMsgStream()
                        .println("Project " + javaProject.getElementName() + " does not exist!");
                continue;
            }

            try {
                IClasspathEntry[] cpe = javaProject.getResolvedClasspath(true);

                for (int j = 0; j < cpe.length; j++) {
                    IClasspathEntry entry = cpe[j];
                    if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                        // TODO Doesn't handle path inclusion/exclusion constraints
                        if (entry.getPath().segmentCount() == 1)
                            ReleaseEngineeringPlugin.getMsgStream().println("*** Ignoring source path entry "
                                    + entry.getPath().toPortableString() + " because it's at a project root.");
                        else
                            fSrcRoots.add(wsRoot.getFolder(entry.getPath()));
                    }
                }
            } catch (JavaModelException e) {
                ReleaseEngineeringPlugin.getMsgStream()
                        .println("Exception encountered while traversing resources:\n  " + e.getMessage());
                logError(e);
            }
        }
    }

    public static String getFileContents(Reader reader) {
        // In this case we don't know the length in advance, so we have to
        // accumulate the reader's contents one buffer at a time.
        StringBuilder sb = new StringBuilder(4096);
        char[] buff = new char[4096];
        int len;

        while (true) {
            try {
                len = reader.read(buff);
            } catch (IOException e) {
                break;
            }
            if (len < 0)
                break;
            sb.append(buff, 0, len);
        }
        return sb.toString();
    }

    private void logError(Exception e) {
        final Status status = new Status(IStatus.ERROR, ReleaseEngineeringPlugin.kPluginID, 0, e.getMessage(), e);
        ReleaseEngineeringPlugin.getInstance().getLog().log(status);
    }
}