Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2013-05-02 08:59:34 +0000
committerEike Stepper2013-05-03 19:06:55 +0000
commitc9c9f58577c9ef76461a8d21b79ec6fb9f60ffe0 (patch)
treefc4c3bebf4f651320cf84dc0515fbb424f345615
parentdbe3ca84739cb36b106ec86cf59916a2eec3a9d0 (diff)
downloadcdo-c9c9f58577c9ef76461a8d21b79ec6fb9f60ffe0.tar.gz
cdo-c9c9f58577c9ef76461a8d21b79ec6fb9f60ffe0.tar.xz
cdo-c9c9f58577c9ef76461a8d21b79ec6fb9f60ffe0.zip
Add GitCopyrightsAction
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/META-INF/MANIFEST.MF2
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/icons/Copyrights.gifbin0 -> 528 bytes
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/icons/test.txt1
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/plugin.xml22
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/GitBash.java4
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/CheckCopyrightsAction.java27
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/GitCopyrightsAction.java442
-rw-r--r--plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/UpdateCopyrightsAction.java539
8 files changed, 942 insertions, 95 deletions
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.cdo.releng.gitbash/META-INF/MANIFEST.MF
index 5da589ef73..990450d3c3 100644
--- a/plugins/org.eclipse.emf.cdo.releng.gitbash/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/META-INF/MANIFEST.MF
@@ -8,7 +8,9 @@ Bundle-Localization: plugin
Bundle-Activator: org.eclipse.emf.cdo.releng.gitbash.Activator
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.filesystem;bundle-version="[1.3.0,2.0.0)",
org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
org.eclipse.team.ui;bundle-version="[3.5.0,4.0.0)",
org.eclipse.jgit;bundle-version="[2.0.0,3.0.0)",
org.eclipse.egit.core;bundle-version="[2.0.0,3.0.0)",
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/Copyrights.gif b/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/Copyrights.gif
new file mode 100644
index 0000000000..daa3652e9b
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/Copyrights.gif
Binary files differ
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/test.txt b/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/test.txt
deleted file mode 100644
index 8a20ac0c6c..0000000000
--- a/plugins/org.eclipse.emf.cdo.releng.gitbash/icons/test.txt
+++ /dev/null
@@ -1 +0,0 @@
-Content1
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/plugin.xml b/plugins/org.eclipse.emf.cdo.releng.gitbash/plugin.xml
index 63625c7432..1e39e3238b 100644
--- a/plugins/org.eclipse.emf.cdo.releng.gitbash/plugin.xml
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<!--
- Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ Copyright (c) 2012, 2013 Eike Stepper (Berlin, Germany) and others.
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
@@ -20,15 +20,25 @@
id="org.eclipse.emf.cdo.releng.gitbash.contribution1"
objectClass="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
<action
- class="org.eclipse.emf.cdo.releng.gitbash.repository.GitCopyrightsAction"
+ class="org.eclipse.emf.cdo.releng.gitbash.repository.CheckCopyrightsAction"
enablesFor="1"
- icon="icons/gitbash.gif"
- id="org.eclipse.emf.cdo.releng.gitbash.GitCopyrightsAction"
- label="Git Copyrights"
+ icon="icons/Copyrights.gif"
+ id="org.eclipse.emf.cdo.releng.gitbash.CheckCopyrightsAction"
+ label="Check Copyrights"
+ menubarPath="additions"
+ state="true"
+ style="push"
+ tooltip="Search for files with missing copyrights"/>
+ <action
+ class="org.eclipse.emf.cdo.releng.gitbash.repository.UpdateCopyrightsAction"
+ enablesFor="1"
+ icon="icons/Copyrights.gif"
+ id="org.eclipse.emf.cdo.releng.gitbash.UpdateCopyrightsAction"
+ label="Update Copyrights"
menubarPath="additions"
state="true"
style="push"
- tooltip="Execute git log"/>
+ tooltip="Update copyrights with years from git log"/>
<action
class="org.eclipse.emf.cdo.releng.gitbash.repository.GitCleanAction"
enablesFor="1"
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/GitBash.java b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/GitBash.java
index 1247d952a3..36d8f578f5 100644
--- a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/GitBash.java
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/GitBash.java
@@ -92,18 +92,18 @@ public class GitBash
if (exitValue == 0)
{
String message = "Command '" + command + "' executed successfully";
- Activator.log(message + "\n" + output, IStatus.INFO);
if (!quiet)
{
+ Activator.log(message + "\n" + output, IStatus.INFO);
MessageDialog.openInformation(shell, "Git Bash", message);
}
}
else
{
String message = "Command '" + command + "' failed: " + exitValue;
- Activator.log(message + "\n" + output, IStatus.ERROR);
if (!quiet)
{
+ Activator.log(message + "\n" + output, IStatus.ERROR);
MessageDialog.openError(shell, "Git Bash", message);
}
}
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/CheckCopyrightsAction.java b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/CheckCopyrightsAction.java
new file mode 100644
index 0000000000..b6e59b1722
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/CheckCopyrightsAction.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Eike Stepper (Berlin, Germany) and others.
+ * 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.releng.gitbash.repository;
+
+/**
+ * @author Eike Stepper
+ */
+public class CheckCopyrightsAction extends UpdateCopyrightsAction
+{
+ public CheckCopyrightsAction()
+ {
+ }
+
+ @Override
+ protected boolean isCheckOnly()
+ {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/GitCopyrightsAction.java b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/GitCopyrightsAction.java
index abd8bd477f..a36549d028 100644
--- a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/GitCopyrightsAction.java
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/GitCopyrightsAction.java
@@ -15,23 +15,48 @@ import org.eclipse.emf.cdo.releng.gitbash.GitBash;
import org.eclipse.swt.widgets.Shell;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
+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;
/**
* @author Eike Stepper
*/
public class GitCopyrightsAction extends AbstractRepositoryAction
{
+ private static final String[] IGNORED_PATHS = { "resourcemanager.java", "menucardtemplate.java",
+ "org.eclipse.emf.cdo.releng.version.tests\\tests", "org.eclipse.net4j.jms.api" };
+
+ private static final String[] REQUIRED_EXTENSIONS = { ".java", ".ant", "build.xml", "plugin.xml", "fragment.xml",
+ "feature.xml", "plugin.properties", "fragment.properties", "feature.properties", "about.properties",
+ "build.properties", "messages.properties", "copyright.txt", ".exsd" };
+
+ private static final String[] OPTIONAL_EXTENSIONS = { ".properties", ".xml", ".html", ".ecore", ".genmodel" };
+
+ private static final String[] IGNORED_MESSAGES = { "update copyrights", "updated copyrights", "adjust legal headers",
+ "adjusted legal headers", "update legal headers", "updated legal headers", "adjust copyrights",
+ "adjusted copyrights", "fix copyrights", "fixed copyrights", "fix legal headers", "fixed legal headers" };
+
+ private static final Pattern COPYRIGHT_PATTERN = Pattern
+ .compile("(.*?)Copyright \\(c\\) ([0-9 ,-]+) (.*?) and others.(.*)");
+
private static final String BEGIN_COMMIT = "--BEGIN-COMMIT--";
- private static final String BEGIN_MESSAGE = "--BEGIN-MESSAGE--";
+ private static final String BEGIN_MESSAGE = "--BEGIN-SUMMARY--";
// "--BEGIN-COMMIT--
// committer date
@@ -40,99 +65,379 @@ public class GitCopyrightsAction extends AbstractRepositoryAction
// summary (multiline)"
private static final String OUTPUT_FORMAT = BEGIN_COMMIT + "%n%ci%n%B%n" + BEGIN_MESSAGE + "%n";
+ private static final String NL = System.getProperty("line.separator");
+
+ private File workTree;
+
+ private int workTreeLength;
+
+ private List<String> missingCopyrights = new ArrayList<String>();
+
+ private int rewriteCount;
+
+ private File outFile;
+
@Override
protected void run(Shell shell, File workTree) throws Exception
{
- // File outFile = File.createTempFile("copyrights-", ".tmp");
- File outFile = new File("/develop/copyrights.txt");
+ try
+ {
+ this.workTree = workTree;
+ workTreeLength = workTree.getAbsolutePath().length() + 1;
+
+ checkFolder(shell, workTree);
+ System.out.println("Missing count: " + missingCopyrights.size());
+ System.out.println("Rewrite count: " + rewriteCount);
+ }
+ finally
+ {
+ missingCopyrights.clear();
+ rewriteCount = 0;
+ outFile = null;
+ }
+ }
+
+ private void checkFolder(Shell shell, File folder) throws Exception
+ {
+ for (File file : folder.listFiles())
+ {
+ String fileName = file.getName();
+ if (file.isDirectory() && !fileName.equals(".git"))
+ {
+ checkFolder(shell, file);
+ }
+ else if (!fileName.equals(".gitlog"))
+ {
+ checkFile(shell, file);
+ }
+ }
+ }
+
+ private void checkFile(Shell shell, File file) throws Exception
+ {
+ if (hasString(file.getPath(), IGNORED_PATHS))
+ {
+ return;
+ }
+
+ boolean required = hasExtension(file, REQUIRED_EXTENSIONS);
+ boolean optional = required || hasExtension(file, OPTIONAL_EXTENSIONS);
+
+ if (optional)
+ {
+ FileReader fileReader = new FileReader(file);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ try
+ {
+ int lineNumber = 0;
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ Matcher matcher = COPYRIGHT_PATTERN.matcher(line);
+ if (matcher.matches())
+ {
+ String prefix = matcher.group(1);
+ String dates = matcher.group(1);
+ String owner = matcher.group(3);
+ String suffix = matcher.group(4);
+
+ rewriteFile(shell, file, lineNumber, prefix, dates, owner, suffix);
+ return;
+ }
+
+ ++lineNumber;
+ }
+
+ if (required)
+ {
+ String path = getPath(file);
+ missingCopyrights.add(path);
+ System.out.println("COPYRIGHT MISSING: " + path);
+ }
+ }
+ finally
+ {
+ bufferedReader.close();
+ fileReader.close();
+ }
+ }
+ }
+
+ private String getPath(File file)
+ {
+ String path = file.getAbsolutePath().replace('\\', '/');
+ return path.substring(workTreeLength);
+ }
+
+ private boolean hasString(String string, String[] strings)
+ {
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (string.indexOf(strings[i]) != -1)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean hasExtension(File file, String[] extensions)
+ {
+ String fileName = file.getName();
+ for (int i = 0; i < extensions.length; i++)
+ {
+ if (fileName.endsWith(extensions[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void rewriteFile(Shell shell, File file, int copyrightLineNumber, String prefix, String dates, String owner,
+ String suffix) throws Exception
+ {
+ String path = getPath(file);
+
+ if (outFile == null)
+ {
+ outFile = File.createTempFile("git-", ".log");
+ }
try
{
GitBash.quiet = true;
- GitBash.executeCommand(shell, workTree, "git log --name-only --format=\"" + OUTPUT_FORMAT + "\" > \"/"
- + outFile.getAbsoluteFile().toString().replace(":", "").replace("\\", "/") + "\"");
+ GitBash.executeCommand(shell, workTree, "git log --follow --name-only --format=\"" + OUTPUT_FORMAT + "\" -- \""
+ + path + "\" > \"/" + outFile.getAbsolutePath().replace(":", "").replace("\\", "/") + "\"");
}
finally
{
GitBash.quiet = false;
}
- parseFile(outFile);
+ Set<Integer> years = parseOutFile();
+ String newDates = formatYears(years);
+ if (newDates.equals(dates))
+ {
+ return;
+ }
+
+ ++rewriteCount;
+ String copyrightLine = prefix + "Copyright (c) " + newDates + " " + owner + " and others." + suffix;
+ System.out.println(path + ": " + newDates);
+
+ List<String> lines = readLines(file);
+ lines.set(copyrightLineNumber, copyrightLine);
+
+ writeLines(file, lines);
}
- private void parseFile(File file) throws FileNotFoundException, IOException
+ private List<String> readLines(File file) throws FileNotFoundException, IOException
{
- Map<String, int[]> years = new HashMap<String, int[]>();
+ Reader fileReader = null;
+ BufferedReader bufferedReader = null;
- FileReader fileReader = new FileReader(file);
- BufferedReader bufferedReader = new BufferedReader(fileReader);
+ try
+ {
+ fileReader = new FileReader(file);
+ bufferedReader = new BufferedReader(fileReader);
+
+ List<String> lines = new ArrayList<String>();
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ lines.add(line);
+ }
+
+ return lines;
+ }
+ finally
+ {
+ close(bufferedReader);
+ close(fileReader);
+ }
+ }
+
+ private void writeLines(File file, List<String> lines) throws IOException
+ {
+ Writer fileWriter = null;
+ BufferedWriter bufferedWriter = null;
try
{
- LogEntry logEntry;
+ fileWriter = new FileWriter(file);
+ bufferedWriter = new BufferedWriter(fileWriter);
- // Start of file. First line has to be "--BEGIN-COMMIT--"
- String line = bufferedReader.readLine();
- if (line == null)
+ for (String line : lines)
{
- return; // Empty log
+ bufferedWriter.write(line);
+ bufferedWriter.write(NL);
}
+ }
+ finally
+ {
+ close(bufferedWriter);
+ close(fileWriter);
+ }
+ }
- if (!line.equals(BEGIN_COMMIT))
+ private void close(Closeable closeable)
+ {
+ if (closeable != null)
+ {
+ try
+ {
+ closeable.close();
+ }
+ catch (IOException ex)
{
- throw new IllegalStateException("Read unexpected line " + line + " at beginning of file "
- + file.getAbsolutePath());
+ ex.printStackTrace();
}
+ }
+ }
+
+ private String formatYears(Collection<Integer> years)
+ {
+ class YearRange
+ {
+ private int begin;
- // First line successfully read. Start processing of log entries:
+ private int end;
- processing: //
- for (;;)
+ public YearRange(int begin)
{
- String date = readLineSafe(bufferedReader);
- logEntry = new LogEntry(date);
+ this.begin = begin;
+ end = begin;
+ }
+
+ public boolean add(int year)
+ {
+ if (year == end + 1)
+ {
+ end = year;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ if (begin == end)
+ {
+ return "" + begin;
+ }
- // Follows the message until the summary marker is read
- StringBuilder builder = new StringBuilder();
- while (!(line = readLineSafe(bufferedReader)).equals(BEGIN_MESSAGE))
+ if (begin == end - 1)
{
- builder.append(line);
- builder.append("\n");
+ return "" + begin + ", " + end;
}
- logEntry.setMessage(builder.toString());
+ return "" + begin + "-" + end;
+ }
+ }
+
+ List<Integer> list = new ArrayList<Integer>(years);
+ Collections.sort(list);
- summaryReading: //
+ List<YearRange> ranges = new ArrayList<YearRange>();
+ YearRange lastRange = null;
+ for (Integer year : list)
+ {
+ if (lastRange == null || !lastRange.add(year))
+ {
+ lastRange = new YearRange(year);
+ ranges.add(lastRange);
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ for (YearRange range : ranges)
+ {
+ if (builder.length() != 0)
+ {
+ builder.append(", ");
+ }
+
+ builder.append(range);
+ }
+
+ return builder.toString();
+ }
+
+ private Set<Integer> parseOutFile() throws Exception
+ {
+ Set<Integer> years = new HashSet<Integer>();
+
+ FileReader fileReader = new FileReader(outFile);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ try
+ {
+ LogEntry logEntry;
+
+ // Start of file. First line has to be "--BEGIN-COMMIT--"
+ String line = bufferedReader.readLine();
+ if (line != null)
+ {
+ if (!line.equals(BEGIN_COMMIT))
+ {
+ throw new IllegalStateException("Read unexpected line " + line + " at beginning of file "
+ + outFile.getAbsolutePath());
+ }
+
+ // First line successfully read. Start processing of log entries
+ processing: //
for (;;)
{
- line = bufferedReader.readLine();
- if (line == null)
- {
- handleLogEntry(years, logEntry);
- break processing; // End of file reached
- }
+ String date = readLineSafe(bufferedReader);
+ logEntry = new LogEntry(date);
- if (line.equals(BEGIN_COMMIT))
+ // Follows the message until the summary marker is read
+ StringBuilder builder = new StringBuilder();
+ while (!(line = readLineSafe(bufferedReader)).equals(BEGIN_MESSAGE))
{
- handleLogEntry(years, logEntry);
- break summaryReading; // End of summary section reached
+ builder.append(line);
+ builder.append("\n");
}
- if (line.trim().length() == 0)
+ logEntry.setMessage(builder.toString());
+
+ summaryReading: //
+ for (;;)
{
- continue; // Read over empty lines
+ line = bufferedReader.readLine();
+ if (line == null)
+ {
+ handleLogEntry(years, logEntry);
+ break processing; // End of file reached
+ }
+
+ if (line.equals(BEGIN_COMMIT))
+ {
+ handleLogEntry(years, logEntry);
+ break summaryReading; // End of summary section reached
+ }
+
+ if (line.trim().length() == 0)
+ {
+ continue; // Read over empty lines
+ }
}
-
- // We are in the summary section. Read line should contain a path
- logEntry.getPaths().add(line);
}
}
+
+ return years;
}
finally
{
bufferedReader.close();
fileReader.close();
- file.delete();
+ outFile.delete();
}
}
@@ -147,44 +452,16 @@ public class GitCopyrightsAction extends AbstractRepositoryAction
return result;
}
- private void handleLogEntry(Map<String, int[]> years, LogEntry logEntry)
+ private void handleLogEntry(Set<Integer> years, LogEntry logEntry)
{
- if (logEntry.getMessage().equalsIgnoreCase("Update copyrights\n"))
+ String message = logEntry.getMessage().toLowerCase();
+ if (hasString(message, IGNORED_MESSAGES))
{
return;
}
int year = Integer.parseInt(logEntry.getDate().substring(0, 4));
-
- for (String path : logEntry.getPaths())
- {
- handlePath(years, year, path);
- }
- }
-
- private void handlePath(Map<String, int[]> years, int year, String path)
- {
- int[] array = years.get(path);
- if (array == null)
- {
- years.put(path, new int[] { year });
- }
- else
- {
- for (int i = 0; i < array.length; i++)
- {
- if (array[i] == year)
- {
- return;
- }
- }
-
- int[] newArray = new int[array.length + 1];
- System.arraycopy(array, 0, newArray, 0, array.length);
- newArray[array.length] = year;
-
- years.put(path, newArray);
- }
+ years.add(year);
}
/**
@@ -196,8 +473,6 @@ public class GitCopyrightsAction extends AbstractRepositoryAction
private String message;
- private List<String> paths = new ArrayList<String>();
-
public LogEntry(String date)
{
this.date = date;
@@ -222,10 +497,5 @@ public class GitCopyrightsAction extends AbstractRepositoryAction
{
this.message = message;
}
-
- public List<String> getPaths()
- {
- return paths;
- }
}
}
diff --git a/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/UpdateCopyrightsAction.java b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/UpdateCopyrightsAction.java
new file mode 100644
index 0000000000..e1f092c382
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.releng.gitbash/src/org/eclipse/emf/cdo/releng/gitbash/repository/UpdateCopyrightsAction.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) Eike Stepper (Berlin, Germany) and others.
+ * 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.releng.gitbash.repository;
+
+import org.eclipse.emf.cdo.releng.gitbash.AbstractAction;
+import org.eclipse.emf.cdo.releng.gitbash.Activator;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Eike Stepper
+ */
+public class UpdateCopyrightsAction extends AbstractAction<Repository>
+{
+ private static final IWorkbench WORKBENCH = PlatformUI.getWorkbench();
+
+ private static final String[] IGNORED_PATHS = { "resourcemanager.java", "menucardtemplate.java",
+ "org.eclipse.emf.cdo.releng.version.tests/tests", "org.eclipse.net4j.jms.api/src" };
+
+ private static final String[] REQUIRED_EXTENSIONS = { ".java", ".ant", "build.xml", "plugin.xml", "fragment.xml",
+ "feature.xml", "plugin.properties", "fragment.properties", "feature.properties", "about.properties",
+ "build.properties", "messages.properties", "copyright.txt", ".exsd", "org.eclipse.jdt.ui.prefs" };
+
+ private static final String[] OPTIONAL_EXTENSIONS = { ".properties", ".xml", ".html", ".css", ".ecore", ".genmodel",
+ ".mwe", ".xpt", ".ext" };
+
+ private static final String[] IGNORED_MESSAGES = { "update copyrights", "updated copyrights", "adjust legal headers",
+ "adjusted legal headers", "update legal headers", "updated legal headers", "adjust copyrights",
+ "adjusted copyrights", "fix copyrights", "fixed copyrights", "fix legal headers", "fixed legal headers" };
+
+ private static final Pattern COPYRIGHT_PATTERN = Pattern
+ .compile("(.*?)Copyright \\(c\\) ([0-9 ,-]+) (.*?) and others\\.(.*)");
+
+ private static final Calendar CALENDAR = GregorianCalendar.getInstance();
+
+ private static final int CURRENT_YEAR = CALENDAR.get(Calendar.YEAR);
+
+ private static final String NL = System.getProperty("line.separator");
+
+ private Git git;
+
+ private File workTree;
+
+ private int workTreeLength;
+
+ private List<String> missingCopyrights = new ArrayList<String>();
+
+ private int rewriteCount;
+
+ public UpdateCopyrightsAction()
+ {
+ super(Repository.class);
+ }
+
+ protected boolean isCheckOnly()
+ {
+ return false;
+ }
+
+ @Override
+ protected void run(final Shell shell, final Repository repository) throws Exception
+ {
+ IRunnableWithProgress runnable = new IRunnableWithProgress()
+ {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException
+ {
+ try
+ {
+ git = new Git(repository);
+ workTree = repository.getWorkTree();
+ workTreeLength = workTree.getAbsolutePath().length() + 1;
+
+ int totalWork = countFiles(workTree);
+ monitor.beginTask("Copyrights", totalWork);
+
+ final long start = System.currentTimeMillis();
+ checkFolder(monitor, workTree);
+
+ shell.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ openEditors(shell, workTree, formatDuration(start));
+ }
+ catch (Exception ex)
+ {
+ Activator.log(ex);
+ }
+ }
+ });
+ }
+ catch (OperationCanceledException ex)
+ {
+ // Do nothing
+ }
+ catch (Exception ex)
+ {
+ throw new InvocationTargetException(ex);
+ }
+ finally
+ {
+ missingCopyrights.clear();
+ rewriteCount = 0;
+ workTree = null;
+ git = null;
+ monitor.done();
+ }
+ }
+ };
+
+ WORKBENCH.getProgressService().run(true, true, runnable);
+ }
+
+ private int countFiles(File folder) throws Exception
+ {
+ int count = 0;
+ for (File file : folder.listFiles())
+ {
+ String fileName = file.getName();
+ if (file.isDirectory() && !fileName.equals(".git"))
+ {
+ count += countFiles(file);
+ }
+ else
+ {
+ String path = getPath(file);
+ if (hasString(path, IGNORED_PATHS))
+ {
+ continue;
+ }
+
+ ++count;
+ }
+ }
+
+ return count;
+ }
+
+ private void checkFolder(IProgressMonitor monitor, File folder) throws Exception
+ {
+ for (File file : folder.listFiles())
+ {
+ if (monitor.isCanceled())
+ {
+ throw new OperationCanceledException();
+ }
+
+ String fileName = file.getName();
+ if (file.isDirectory() && !fileName.equals(".git"))
+ {
+ checkFolder(monitor, file);
+ }
+ else
+ {
+ if (checkFile(file))
+ {
+ monitor.worked(1);
+ }
+ }
+ }
+ }
+
+ private boolean checkFile(File file) throws Exception
+ {
+ String path = getPath(file);
+ if (hasString(path, IGNORED_PATHS))
+ {
+ return false;
+ }
+
+ boolean required = hasExtension(file, REQUIRED_EXTENSIONS);
+ boolean optional = required || hasExtension(file, OPTIONAL_EXTENSIONS);
+
+ if (optional)
+ {
+ List<String> lines = new ArrayList<String>();
+ boolean copyrightFound = false;
+ boolean copyrightChanged = false;
+
+ FileReader fileReader = new FileReader(file);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ try
+ {
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ Matcher matcher = COPYRIGHT_PATTERN.matcher(line);
+ if (matcher.matches())
+ {
+ copyrightFound = true;
+ if (isCheckOnly())
+ {
+ break;
+ }
+
+ String prefix = matcher.group(1);
+ String dates = matcher.group(2);
+ String owner = matcher.group(3);
+ String suffix = matcher.group(4);
+
+ String newLine = rewriteCopyright(path, line, prefix, dates, owner, suffix);
+ if (newLine != line)
+ {
+ line = newLine;
+ copyrightChanged = true;
+ }
+ }
+
+ lines.add(line);
+ }
+ }
+ finally
+ {
+ close(bufferedReader);
+ close(fileReader);
+ }
+
+ if (required && !copyrightFound)
+ {
+ missingCopyrights.add(path);
+ }
+
+ if (copyrightChanged)
+ {
+ writeLines(file, lines);
+ ++rewriteCount;
+ }
+ }
+
+ return true;
+ }
+
+ private String rewriteCopyright(String path, String line, String prefix, String dates, String owner, String suffix)
+ throws Exception
+ {
+ String newDates;
+
+ if (path.endsWith("org.eclipse.jdt.ui.prefs") || path.endsWith("copyright.txt")
+ || path.endsWith("org.eclipse.emf.cdo.license-feature/feature.properties")
+ || suffix.equals(" All rights reserved.\\n\\"))
+ {
+ // Special handling of occurrences with a more global (then file-scoped) meaning.
+ // Special handling of second occurrence in about.properties (which is visible in the UI).
+ newDates = "2004-" + CURRENT_YEAR;
+ }
+ else
+ {
+ Set<Integer> years = new HashSet<Integer>();
+
+ for (RevCommit commit : git.log().addPath(path).call())
+ {
+ String message = commit.getFullMessage().toLowerCase();
+ if (!hasString(message, IGNORED_MESSAGES))
+ {
+ CALENDAR.setTimeInMillis(1000L * commit.getCommitTime());
+ int year = CALENDAR.get(Calendar.YEAR);
+ years.add(year);
+ }
+ }
+
+ newDates = formatYears(years);
+ }
+
+ if (newDates.equals(dates))
+ {
+ return line;
+ }
+
+ return prefix + "Copyright (c) " + newDates + " " + owner + " and" + " others." + suffix;
+ }
+
+ private String formatYears(Collection<Integer> years)
+ {
+ class YearRange
+ {
+ private int begin;
+
+ private int end;
+
+ public YearRange(int begin)
+ {
+ this.begin = begin;
+ end = begin;
+ }
+
+ public boolean add(int year)
+ {
+ if (year == end + 1)
+ {
+ end = year;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ if (begin == end)
+ {
+ return "" + begin;
+ }
+
+ if (begin == end - 1)
+ {
+ return "" + begin + ", " + end;
+ }
+
+ return "" + begin + "-" + end;
+ }
+ }
+
+ List<Integer> list = new ArrayList<Integer>(years);
+ Collections.sort(list);
+
+ List<YearRange> ranges = new ArrayList<YearRange>();
+ YearRange lastRange = null;
+ for (Integer year : list)
+ {
+ if (lastRange == null || !lastRange.add(year))
+ {
+ lastRange = new YearRange(year);
+ ranges.add(lastRange);
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ for (YearRange range : ranges)
+ {
+ if (builder.length() != 0)
+ {
+ builder.append(", ");
+ }
+
+ builder.append(range);
+ }
+
+ return builder.toString();
+ }
+
+ private String getPath(File file)
+ {
+ String path = file.getAbsolutePath().replace('\\', '/');
+ return path.substring(workTreeLength);
+ }
+
+ private boolean hasString(String string, String[] strings)
+ {
+ string = string.toLowerCase();
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (string.indexOf(strings[i]) != -1)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean hasExtension(File file, String[] extensions)
+ {
+ String fileName = file.getName().toLowerCase();
+ for (int i = 0; i < extensions.length; i++)
+ {
+ if (fileName.endsWith(extensions[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void openEditors(Shell shell, File workTree, String duration) throws PartInitException
+ {
+ String message = "Missing count: " + missingCopyrights.size();
+ message += "\nRewrite count: " + rewriteCount;
+ message += "\nTime needed: " + duration;
+
+ int size = missingCopyrights.size();
+ if (size != 0)
+ {
+ StringBuilder builder = new StringBuilder(message);
+ message += "\n\nCopyrights are missing in " + size + " files.\nDo you want to open them in editors?";
+ if (MessageDialog.openQuestion(shell, "Missing Copyrights", message))
+ {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IWorkbenchPage page = WORKBENCH.getActiveWorkbenchWindow().getActivePage();
+
+ builder.append("\n");
+ for (String missingCopyright : missingCopyrights)
+ {
+ builder.append("\nMissing: ");
+ builder.append(missingCopyright);
+
+ File externalFile = new File(workTree, missingCopyright);
+ IFile file = root.getFileForLocation(new Path(externalFile.getAbsolutePath()));
+ if (file != null && file.isAccessible())
+ {
+ IDE.openEditor(page, file);
+ }
+ else
+ {
+ IFileStore fileStore = EFS.getLocalFileSystem().getStore(externalFile.toURI());
+ if (fileStore != null)
+ {
+ IDE.openEditorOnFileStore(page, fileStore);
+ }
+ }
+ }
+
+ Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, builder.toString()));
+ }
+ }
+ else
+ {
+ MessageDialog.openInformation(shell, "Copyrights", message);
+ Activator.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, message));
+ }
+ }
+
+ private static String formatDuration(long start)
+ {
+ double duration = System.currentTimeMillis() - start;
+
+ String unit = "milliseconds";
+ if (duration > 1000d)
+ {
+ duration = duration / 1000d;
+ unit = "seconds";
+
+ if (duration > 60d)
+ {
+ duration = duration / 60d;
+ unit = "minutes";
+
+ if (duration > 60d)
+ {
+ duration = duration / 60d;
+ unit = "hours";
+ }
+ }
+ }
+
+ duration = Math.round(duration * 100d) / 100d;
+ return duration + " " + unit;
+ }
+
+ private static void writeLines(File file, List<String> lines) throws IOException
+ {
+ Writer fileWriter = null;
+ BufferedWriter bufferedWriter = null;
+
+ try
+ {
+ fileWriter = new FileWriter(file);
+ bufferedWriter = new BufferedWriter(fileWriter);
+
+ for (String line : lines)
+ {
+ bufferedWriter.write(line);
+ bufferedWriter.write(NL);
+ }
+ }
+ finally
+ {
+ close(bufferedWriter);
+ close(fileWriter);
+ }
+ }
+
+ private static void close(Closeable closeable)
+ {
+ if (closeable != null)
+ {
+ try
+ {
+ closeable.close();
+ }
+ catch (IOException ex)
+ {
+ Activator.log(ex);
+ }
+ }
+ }
+}

Back to the top