This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to annotated tag japicmp-base-0.0.1
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git

commit 0ed6bdc36c762c04f724510102237a1c5760b59e
Author: mmois <martin.m...@googlemail.com>
AuthorDate: Sun Sep 29 13:31:27 2013 +0200

    Initial commit
---
 .gitignore                                         |   8 ++
 japicmp/pom.xml                                    |  56 +++++++++++
 japicmp/src/main/java/japicmp/JApiCmp.java         |  62 +++++++++++++
 japicmp/src/main/java/japicmp/cli/CliParser.java   |  63 +++++++++++++
 .../src/main/java/japicmp/cmp/ClassComparator.java |  72 ++++++++++++++
 .../main/java/japicmp/cmp/ClassesComparator.java   |  64 +++++++++++++
 .../java/japicmp/cmp/JarArchiveComparator.java     |  83 +++++++++++++++++
 japicmp/src/main/java/japicmp/config/Options.java  |  42 +++++++++
 .../main/java/japicmp/model/JApiChangeStatus.java  |   5 +
 japicmp/src/main/java/japicmp/model/JApiClass.java |  83 +++++++++++++++++
 .../src/main/java/japicmp/model/JApiMethod.java    |  61 ++++++++++++
 .../src/main/java/japicmp/model/JApiParameter.java |  16 ++++
 .../java/japicmp/output/OutputTransformer.java     |  55 +++++++++++
 .../output/stdout/StdoutOutputGenerator.java       |  71 ++++++++++++++
 .../japicmp/output/xml/XmlOutputGenerator.java     |  24 +++++
 .../japicmp/output/xml/model/JApiCmpXmlRoot.java   |  43 +++++++++
 .../main/java/japicmp/util/SignatureParser.java    | 103 +++++++++++++++++++++
 .../java/japicmp/util/StringArrayEnumeration.java  |  27 ++++++
 japicmp/src/main/resources/log4j.properties        |   8 ++
 .../src/test/java/japicmp/cli/CliParserTest.java   |  40 ++++++++
 .../java/japicmp/util/SignatureParserTest.java     |  99 ++++++++++++++++++++
 .../japicmp/util/StringArrayEnumerationTest.java   |  37 ++++++++
 pom.xml                                            |  43 +++++++++
 23 files changed, 1165 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e2e9750
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+target
+.idea
+httpclient*.jar
+*.iml
+test.xml
+output.xml
+japicmp/guava-10.0.1.jar
+japicmp/guava-14.0.1.jar
diff --git a/japicmp/pom.xml b/japicmp/pom.xml
new file mode 100644
index 0000000..f26ebbf
--- /dev/null
+++ b/japicmp/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>japicmp</groupId>
+        <artifactId>japicmp-base</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>japicmp</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>3.12.1.GA</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.17</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>14.0.1</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.5.2</version>
+                <configuration>
+                    <formats>
+                        <format>html</format>
+                    </formats>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>clean</goal>
+                            <goal>cobertura</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/japicmp/src/main/java/japicmp/JApiCmp.java 
b/japicmp/src/main/java/japicmp/JApiCmp.java
new file mode 100644
index 0000000..70c861f
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/JApiCmp.java
@@ -0,0 +1,62 @@
+package japicmp;
+
+import japicmp.cli.CliParser;
+import japicmp.cmp.JarArchiveComparator;
+import japicmp.config.Options;
+import japicmp.model.JApiClass;
+import japicmp.output.OutputTransformer;
+import japicmp.output.stdout.StdoutOutputGenerator;
+import japicmp.output.xml.XmlOutputGenerator;
+
+import java.io.File;
+import java.util.List;
+
+public class JApiCmp {
+
+    public static void main(String[] args) {
+        Options options = parseCliOptions(args);
+        File oldArchive = new File(options.getOldArchive());
+        File newArchive = new File(options.getNewArchive());
+        verifyFilesExist(oldArchive, newArchive);
+        JarArchiveComparator jarArchiveComparator = new JarArchiveComparator();
+        List<JApiClass> jApiClasses = jarArchiveComparator.compare(oldArchive, 
newArchive);
+        generateOutput(options, oldArchive, newArchive, jApiClasses);
+    }
+
+    private static void generateOutput(Options options, File oldArchive, File 
newArchive, List<JApiClass> jApiClasses) {
+        OutputTransformer.sortClassesAndMethods(jApiClasses);
+        if(options.getXmlOutputFile().isPresent()) {
+            XmlOutputGenerator xmlGenerator = new XmlOutputGenerator();
+            xmlGenerator.generate(oldArchive, newArchive, jApiClasses, 
options);
+        }
+        StdoutOutputGenerator stdoutOutputGenerator = new 
StdoutOutputGenerator();
+        String output = stdoutOutputGenerator.generate(oldArchive, newArchive, 
jApiClasses, options);
+        System.out.println(output);
+    }
+
+    private static Options parseCliOptions(String[] args) {
+        Options options = new Options();
+        try {
+            CliParser cliParser = new CliParser();
+            options = cliParser.parse(args);
+        } catch (IllegalArgumentException e) {
+            System.err.println(e.getMessage());
+            System.exit(-1);
+        } catch (Exception e) {
+            System.err.println("Failed to parse command line options: " + 
e.getMessage());
+            System.exit(-1);
+        }
+        return options;
+    }
+
+    private static void verifyFilesExist(File oldArchive, File newArchive) {
+        if (!oldArchive.exists()) {
+            System.err.println(String.format("File '%s' does not exist.", 
oldArchive.getAbsolutePath()));
+            System.exit(-1);
+        }
+        if (!newArchive.exists()) {
+            System.err.println(String.format("File '%s' does not exist.", 
newArchive.getAbsolutePath()));
+            System.exit(-1);
+        }
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/cli/CliParser.java 
b/japicmp/src/main/java/japicmp/cli/CliParser.java
new file mode 100644
index 0000000..1e1f788
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/cli/CliParser.java
@@ -0,0 +1,63 @@
+package japicmp.cli;
+
+import com.google.common.base.Optional;
+import japicmp.config.Options;
+import japicmp.util.StringArrayEnumeration;
+
+public class CliParser {
+
+    public Options parse(String[] args) throws IllegalArgumentException {
+        Options options = new Options();
+        StringArrayEnumeration sae = new StringArrayEnumeration(args);
+        while (sae.hasMoreElements()) {
+            String arg = sae.nextElement();
+            if ("-n".equals(arg)) {
+                String newArchive = getOptionWithArgument("-n", sae);
+                options.setNewArchive(newArchive);
+            }
+            if ("-o".equals(arg)) {
+                String oldArchive = getOptionWithArgument("-o", sae);
+                options.setOldArchive(oldArchive);
+            }
+            if ("-x".equals(arg)) {
+                String xmlOutputFile = getOptionWithArgument("-x", sae);
+                options.setXmlOutputFile(Optional.of(xmlOutputFile));
+            }
+            if ("-m".equals(arg)) {
+                options.setOutputOnlyModifications(true);
+            }
+            if ("-h".equals(arg)) {
+                System.out.println("Available parameters:");
+                System.out.println("-h                        Prints this 
help.");
+                System.out.println("-o <pathToOldVersionJar>  Provides the 
path to the old version of the jar.");
+                System.out.println("-n <pathToNewVersionJar>  Provides the 
path to the new version of the jar.");
+                System.out.println("-x <pathToXmlOutputFile>  Provides the 
path to the xml output file. If not given, stdout is used.");
+                System.out.println("-m                        Outputs only 
modified classes/methods. If not given, all classes and methods are printed.");
+                System.exit(0);
+            }
+        }
+        checkForMandatoryOptions(options);
+        return options;
+    }
+
+    private void checkForMandatoryOptions(Options options) {
+        if (options.getOldArchive() == null || 
options.getOldArchive().length() == 0) {
+            throw new IllegalArgumentException("Missing option for old 
version: -o <pathToOldVersionJar>");
+        }
+        if (options.getNewArchive() == null || 
options.getNewArchive().length() == 0) {
+            throw new IllegalArgumentException("Missing option for new 
version: -n <pathToNewVersionJar>");
+        }
+    }
+
+    private String getOptionWithArgument(String option, StringArrayEnumeration 
sae) {
+        if (sae.hasMoreElements()) {
+            String value = sae.nextElement();
+            if(value.startsWith("-")) {
+                throw new IllegalArgumentException(String.format("Missing 
argument for option %s.", option));
+            }
+            return value;
+        } else {
+            throw new IllegalArgumentException(String.format("Missing argument 
for option %s.", option));
+        }
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/cmp/ClassComparator.java 
b/japicmp/src/main/java/japicmp/cmp/ClassComparator.java
new file mode 100644
index 0000000..92838e7
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/cmp/ClassComparator.java
@@ -0,0 +1,72 @@
+package japicmp.cmp;
+
+import com.google.common.base.Optional;
+import japicmp.model.JApiChangeStatus;
+import japicmp.model.JApiClass;
+import japicmp.model.JApiMethod;
+import japicmp.model.JApiParameter;
+import japicmp.util.SignatureParser;
+import javassist.CtClass;
+import javassist.CtMethod;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ClassComparator {
+
+    public void compare(JApiClass jApiClass) {
+        Map<String, CtMethod> oldMethodsMap = 
createMethodMap(jApiClass.getOldClass());
+        Map<String, CtMethod> newMethodsMap = 
createMethodMap(jApiClass.getNewClass());
+        sortMethodsIntoLists(oldMethodsMap, newMethodsMap, jApiClass);
+    }
+
+    private void sortMethodsIntoLists(Map<String, CtMethod> oldMethodsMap, 
Map<String, CtMethod> newMethodsMap, JApiClass jApiClass) {
+        SignatureParser signatureParser = new SignatureParser();
+        for (CtMethod ctMethod : oldMethodsMap.values()) {
+            String longName = ctMethod.getLongName();
+            signatureParser.parse(ctMethod.getSignature());
+            CtMethod foundMethod = newMethodsMap.get(longName);
+            if (foundMethod == null) {
+                JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), 
JApiChangeStatus.REMOVED, Optional.of(ctMethod), Optional.<CtMethod>absent(), 
signatureParser.getReturnType());
+                addParametersToMethod(signatureParser, jApiMethod);
+                jApiClass.addMethod(jApiMethod);
+                if(jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
+                    jApiClass.setChangeStatus(JApiChangeStatus.MODIFIED);
+                }
+            } else {
+                JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), 
JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(foundMethod), 
signatureParser.getReturnType());
+                addParametersToMethod(signatureParser, jApiMethod);
+                jApiClass.addMethod(jApiMethod);
+            }
+        }
+        for (CtMethod ctMethod : newMethodsMap.values()) {
+            String longName = ctMethod.getLongName();
+            signatureParser.parse(ctMethod.getSignature());
+            CtMethod foundMethod = oldMethodsMap.get(longName);
+            if (foundMethod == null) {
+                JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), 
JApiChangeStatus.NEW, Optional.<CtMethod>absent(), Optional.of(ctMethod), 
signatureParser.getReturnType());
+                addParametersToMethod(signatureParser, jApiMethod);
+                jApiClass.addMethod(jApiMethod);
+                if(jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
+                    jApiClass.setChangeStatus(JApiChangeStatus.MODIFIED);
+                }
+            }
+        }
+    }
+
+    private void addParametersToMethod(SignatureParser signatureParser, 
JApiMethod jApiMethod) {
+        for (String param : signatureParser.getParameters()) {
+            jApiMethod.addParameter(new JApiParameter(param));
+        }
+    }
+
+    private Map<String, CtMethod> createMethodMap(Optional<CtClass> ctClass) {
+        Map<String, CtMethod> methods = new HashMap<String, CtMethod>();
+        if (ctClass.isPresent()) {
+            for (CtMethod ctMethod : ctClass.get().getMethods()) {
+                methods.put(ctMethod.getLongName(), ctMethod);
+            }
+        }
+        return methods;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java 
b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java
new file mode 100644
index 0000000..ee02733
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java
@@ -0,0 +1,64 @@
+package japicmp.cmp;
+
+import com.google.common.base.Optional;
+import japicmp.model.JApiChangeStatus;
+import japicmp.model.JApiClass;
+import javassist.CtClass;
+import javassist.Modifier;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class ClassesComparator {
+    private List<JApiClass> classes = new LinkedList<JApiClass>();
+
+    public void compare(List<CtClass> oldClassesArg, List<CtClass> 
newClassesArg) {
+        classes = new LinkedList<JApiClass>();
+        Map<String, CtClass> oldClassesMap = createClassMap(oldClassesArg);
+        Map<String, CtClass> newClassesMap = createClassMap(newClassesArg);
+        sortIntoLists(oldClassesMap, newClassesMap);
+    }
+
+    private void sortIntoLists(Map<String, CtClass> oldClassesMap, Map<String, 
CtClass> newClassesMap) {
+        for(CtClass ctClass : oldClassesMap.values()) {
+            CtClass foundClass = newClassesMap.get(ctClass.getName());
+            if(foundClass == null) {
+                classes.add(new JApiClass(ctClass.getName(), 
Optional.<CtClass>of(ctClass), Optional.<CtClass>absent(), 
JApiChangeStatus.REMOVED, getType(ctClass)));
+            } else {
+                classes.add(new JApiClass(ctClass.getName(), 
Optional.<CtClass>of(ctClass), Optional.<CtClass>of(foundClass), 
JApiChangeStatus.UNCHANGED, getType(ctClass)));
+            }
+        }
+        for(CtClass ctClass : newClassesMap.values()) {
+            CtClass foundClass = oldClassesMap.get(ctClass.getName());
+            if(foundClass == null) {
+                classes.add(new JApiClass(ctClass.getName(), 
Optional.<CtClass>absent(), Optional.<CtClass>of(ctClass), 
JApiChangeStatus.NEW, getType(ctClass)));
+            }
+        }
+    }
+
+    private JApiClass.Type getType(CtClass ctClass) {
+        if(ctClass.isAnnotation()) {
+            return JApiClass.Type.ANNOTATION;
+        } else if(ctClass.isEnum()) {
+            return JApiClass.Type.ENUM;
+        } else if(ctClass.isInterface()) {
+            return JApiClass.Type.INTERFACE;
+        } else {
+            return JApiClass.Type.CLASS;
+        }
+    }
+
+    private Map<String, CtClass> createClassMap(List<CtClass> oldClassesArg) {
+        Map<String, CtClass> oldClassesMap = new HashMap<String, CtClass>();
+        for(CtClass ctClass : oldClassesArg) {
+            oldClassesMap.put(ctClass.getName(), ctClass);
+        }
+        return oldClassesMap;
+    }
+
+    public List<JApiClass> getClasses() {
+        return classes;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java 
b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java
new file mode 100644
index 0000000..0f150c2
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java
@@ -0,0 +1,83 @@
+package japicmp.cmp;
+
+import japicmp.model.JApiClass;
+import javassist.ClassPool;
+import javassist.CtClass;
+import org.apache.log4j.Logger;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public class JarArchiveComparator {
+    private static final Logger logger = 
Logger.getLogger(JarArchiveComparator.class);
+
+    public List<JApiClass> compare(File oldArchive, File newArchive) {
+        ClassPool classPool = new ClassPool();
+        try {
+            ClassesComparator classesComparator = 
compareClassLists(oldArchive, newArchive, classPool);
+            List<JApiClass> classList = classesComparator.getClasses();
+            compareClasses(classList);
+            return classList;
+        } catch (Exception e) {
+            System.err.println(String.format("Processing jar files '%s' and 
'%s' failed: %s.", oldArchive.getAbsolutePath(), newArchive.getAbsolutePath(), 
e.getMessage()));
+            return new LinkedList<JApiClass>();
+        }
+    }
+
+    private void compareClasses(List<JApiClass> classList) {
+        for (JApiClass jApiClass : classList) {
+            ClassComparator classComparator = new ClassComparator();
+            classComparator.compare(jApiClass);
+        }
+    }
+
+    private ClassesComparator compareClassLists(File oldArchive, File 
newArchive, ClassPool classPool) throws Exception {
+        List<CtClass> oldClasses = createListOfCtClasses(oldArchive, 
classPool);
+        List<CtClass> newClasses = createListOfCtClasses(newArchive, 
classPool);
+        ClassesComparator classesComparator = new ClassesComparator();
+        classesComparator.compare(oldClasses, newClasses);
+        if (logger.isDebugEnabled()) {
+            for (JApiClass jApiClass : classesComparator.getClasses()) {
+                logger.debug(jApiClass);
+            }
+        }
+        return classesComparator;
+    }
+
+    private List<CtClass> createListOfCtClasses(File archive, ClassPool 
classPool) throws Exception {
+        List<CtClass> classes = new LinkedList<CtClass>();
+        JarFile oldJar = null;
+        try {
+            oldJar = new JarFile(archive);
+            Enumeration<JarEntry> entryEnumeration = oldJar.entries();
+            while (entryEnumeration.hasMoreElements()) {
+                JarEntry jarEntry = entryEnumeration.nextElement();
+                String name = jarEntry.getName();
+                if (name.endsWith(".class")) {
+                    CtClass ctClass = null;
+                    try {
+                        ctClass = 
classPool.makeClass(oldJar.getInputStream(jarEntry));
+                    } catch (Exception e) {
+                        logger.error(String.format("Failed to load file from 
jar '%s' as class file: %s.", name, e.getMessage()));
+                        throw e;
+                    }
+                    classes.add(ctClass);
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(String.format("Adding class '%s' with jar 
name '%s' to list.", ctClass.getName(), name));
+                    }
+                } else {
+                    logger.debug(String.format("Skipping file '%s' because 
filename does not end with '.class'.", name));
+                }
+            }
+        } finally {
+            if (oldJar != null) {
+                oldJar.close();
+            }
+        }
+        return classes;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/config/Options.java 
b/japicmp/src/main/java/japicmp/config/Options.java
new file mode 100644
index 0000000..4576f72
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/config/Options.java
@@ -0,0 +1,42 @@
+package japicmp.config;
+
+import com.google.common.base.Optional;
+
+public class Options {
+    private String oldArchive;
+    private String newArchive;
+    private boolean outputOnlyModifications = false;
+    private Optional<String> xmlOutputFile = Optional.<String>absent();
+
+    public String getNewArchive() {
+        return newArchive;
+    }
+
+    public void setNewArchive(String newArchive) {
+        this.newArchive = newArchive;
+    }
+
+    public String getOldArchive() {
+        return oldArchive;
+    }
+
+    public void setOldArchive(String oldArchive) {
+        this.oldArchive = oldArchive;
+    }
+
+    public boolean isOutputOnlyModifications() {
+        return outputOnlyModifications;
+    }
+
+    public void setOutputOnlyModifications(boolean outputOnlyModifications) {
+        this.outputOnlyModifications = outputOnlyModifications;
+    }
+
+    public Optional<String> getXmlOutputFile() {
+        return xmlOutputFile;
+    }
+
+    public void setXmlOutputFile(Optional<String> xmlOutputFile) {
+        this.xmlOutputFile = xmlOutputFile;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/model/JApiChangeStatus.java 
b/japicmp/src/main/java/japicmp/model/JApiChangeStatus.java
new file mode 100644
index 0000000..8222e7c
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/model/JApiChangeStatus.java
@@ -0,0 +1,5 @@
+package japicmp.model;
+
+public enum JApiChangeStatus {
+    NEW, REMOVED, UNCHANGED, MODIFIED;
+}
diff --git a/japicmp/src/main/java/japicmp/model/JApiClass.java 
b/japicmp/src/main/java/japicmp/model/JApiClass.java
new file mode 100644
index 0000000..e1d429f
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/model/JApiClass.java
@@ -0,0 +1,83 @@
+package japicmp.model;
+
+import com.google.common.base.Optional;
+import javassist.CtClass;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+import java.util.LinkedList;
+import java.util.List;
+
+public class JApiClass {
+    private final String fullyQualifiedName;
+    private final Optional<CtClass> oldClass;
+    private final Optional<CtClass> newClass;
+    private List<JApiMethod> methods = new LinkedList<JApiMethod>();
+    private JApiChangeStatus changeStatus;
+    private final Type type;
+
+    public enum Type {
+        ANNOTATION, INTERFACE, CLASS, ENUM
+    }
+
+    public JApiClass(String fullyQualifiedName, Optional<CtClass> oldClass, 
Optional<CtClass> newClass, JApiChangeStatus changeStatus, Type type) {
+        this.changeStatus = changeStatus;
+        this.fullyQualifiedName = fullyQualifiedName;
+        this.newClass = newClass;
+        this.oldClass = oldClass;
+        this.type = type;
+    }
+
+    public void addMethod(JApiMethod jApiMethod) {
+        methods.add(jApiMethod);
+    }
+
+    @XmlAttribute
+    public JApiChangeStatus getChangeStatus() {
+        return changeStatus;
+    }
+
+    @XmlAttribute
+    public String getFullyQualifiedName() {
+        return fullyQualifiedName;
+    }
+
+    @XmlTransient
+    public Optional<CtClass> getNewClass() {
+        return newClass;
+    }
+
+    @XmlTransient
+    public Optional<CtClass> getOldClass() {
+        return oldClass;
+    }
+
+    public void setChangeStatus(JApiChangeStatus changeStatus) {
+        this.changeStatus = changeStatus;
+    }
+
+    @XmlElement(name = "method")
+    public List<JApiMethod> getMethods() {
+        return methods;
+    }
+
+    public void setMethods(List<JApiMethod> methods) {
+        this.methods = methods;
+    }
+
+    @XmlAttribute
+    public Type getType() {
+        return type;
+    }
+
+    @Override
+    public String toString() {
+        return "JApiClass{" +
+                "changeStatus=" + changeStatus +
+                ", fullyQualifiedName='" + fullyQualifiedName + '\'' +
+                ", oldClass=" + oldClass +
+                ", newClass=" + newClass +
+                '}';
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/model/JApiMethod.java 
b/japicmp/src/main/java/japicmp/model/JApiMethod.java
new file mode 100644
index 0000000..ab04e1f
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/model/JApiMethod.java
@@ -0,0 +1,61 @@
+package japicmp.model;
+
+import com.google.common.base.Optional;
+import javassist.CtMethod;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+import java.util.LinkedList;
+import java.util.List;
+
+public class JApiMethod {
+    private final String name;
+    private final JApiChangeStatus changeStatus;
+    private final Optional<CtMethod> oldMethod;
+    private final Optional<CtMethod> newMethod;
+    private final String returnType;
+    private final List<JApiParameter> parameters = new 
LinkedList<JApiParameter>();
+
+    public JApiMethod(String name, JApiChangeStatus changeStatus, 
Optional<CtMethod> oldClass, Optional<CtMethod> newClass, String returnType) {
+        this.name = name;
+        this.changeStatus = changeStatus;
+        this.oldMethod = oldClass;
+        this.newMethod = newClass;
+        this.returnType = returnType;
+    }
+
+    @XmlAttribute
+    public JApiChangeStatus getChangeStatus() {
+        return changeStatus;
+    }
+
+    @XmlAttribute
+    public String getName() {
+        return name;
+    }
+
+    @XmlTransient
+    public Optional<CtMethod> getNewMethod() {
+        return newMethod;
+    }
+
+    @XmlTransient
+    public Optional<CtMethod> getOldMethod() {
+        return oldMethod;
+    }
+
+    @XmlAttribute
+    public String getReturnType() {
+        return returnType;
+    }
+
+    @XmlElement(name = "parameter")
+    public List<JApiParameter> getParameters() {
+        return parameters;
+    }
+
+    public void addParameter(JApiParameter jApiParameter) {
+        parameters.add(jApiParameter);
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/model/JApiParameter.java 
b/japicmp/src/main/java/japicmp/model/JApiParameter.java
new file mode 100644
index 0000000..56f2a81
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/model/JApiParameter.java
@@ -0,0 +1,16 @@
+package japicmp.model;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class JApiParameter {
+    private final String type;
+
+    public JApiParameter(String type) {
+        this.type = type;
+    }
+
+    @XmlAttribute
+    public String getType() {
+        return type;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/output/OutputTransformer.java 
b/japicmp/src/main/java/japicmp/output/OutputTransformer.java
new file mode 100644
index 0000000..5b31b27
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/output/OutputTransformer.java
@@ -0,0 +1,55 @@
+package japicmp.output;
+
+import japicmp.model.JApiChangeStatus;
+import japicmp.model.JApiClass;
+import japicmp.model.JApiMethod;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class OutputTransformer {
+
+    private OutputTransformer() {
+
+    }
+
+    public static void removeUnchanged(List<JApiClass> jApiClasses) {
+        List<JApiClass> classesToRemove = new LinkedList<JApiClass>();
+        for (JApiClass jApiClass : jApiClasses) {
+            if (jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
+                classesToRemove.add(jApiClass);
+            } else {
+                List<JApiMethod> methodsToRemove = new 
LinkedList<JApiMethod>();
+                List<JApiMethod> methods = jApiClass.getMethods();
+                for (JApiMethod jApiMethod : methods) {
+                    if (jApiMethod.getChangeStatus() == 
JApiChangeStatus.UNCHANGED) {
+                        methodsToRemove.add(jApiMethod);
+                    }
+                }
+                for (JApiMethod jApiMethod : methodsToRemove) {
+                    methods.remove(jApiMethod);
+                }
+            }
+        }
+        for (JApiClass jApiClass : classesToRemove) {
+            jApiClasses.remove(jApiClass);
+        }
+    }
+
+    public static void sortClassesAndMethods(List<JApiClass> jApiClasses) {
+        Collections.sort(jApiClasses, new Comparator<JApiClass>() {
+            public int compare(JApiClass o1, JApiClass o2) {
+                return 
o1.getFullyQualifiedName().compareToIgnoreCase(o2.getFullyQualifiedName());
+            }
+        });
+        for(JApiClass jApiClass : jApiClasses) {
+            Collections.sort(jApiClass.getMethods(), new 
Comparator<JApiMethod>() {
+                public int compare(JApiMethod o1, JApiMethod o2) {
+                    return o1.getName().compareToIgnoreCase(o2.getName());
+                }
+            });
+        }
+    }
+}
diff --git 
a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java 
b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java
new file mode 100644
index 0000000..cead423
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java
@@ -0,0 +1,71 @@
+package japicmp.output.stdout;
+
+import japicmp.config.Options;
+import japicmp.model.JApiChangeStatus;
+import japicmp.model.JApiClass;
+import japicmp.model.JApiMethod;
+import japicmp.model.JApiParameter;
+import japicmp.output.OutputTransformer;
+
+import java.io.File;
+import java.util.List;
+
+public class StdoutOutputGenerator {
+
+    public String generate(File oldArchive, File newArchive, List<JApiClass> 
jApiClasses, Options options) {
+        if (options.isOutputOnlyModifications()) {
+            OutputTransformer.removeUnchanged(jApiClasses);
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(String.format("Comparing %s with %s:\n", 
oldArchive.getAbsolutePath(), newArchive.getAbsolutePath()));
+        for (JApiClass jApiClass : jApiClasses) {
+            processClass(sb, jApiClass);
+            processMethods(sb, jApiClass);
+        }
+        return sb.toString();
+    }
+
+    private void processMethods(StringBuilder sb, JApiClass jApiClass) {
+        List<JApiMethod> methods = jApiClass.getMethods();
+        for (JApiMethod jApiMethod : methods) {
+            if (jApiMethod.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
+                appendMethod(sb, "===", jApiMethod);
+            } else if (jApiMethod.getChangeStatus() == JApiChangeStatus.NEW) {
+                appendMethod(sb, "+++", jApiMethod);
+            } else if (jApiMethod.getChangeStatus() == 
JApiChangeStatus.REMOVED) {
+                appendMethod(sb, "---", jApiMethod);
+            } else if (jApiMethod.getChangeStatus() == 
JApiChangeStatus.MODIFIED) {
+                appendMethod(sb, "***", jApiMethod);
+            }
+        }
+    }
+
+    private void processClass(StringBuilder sb, JApiClass jApiClass) {
+        if (jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
+            appendClass(sb, "===", jApiClass);
+        } else if (jApiClass.getChangeStatus() == JApiChangeStatus.NEW) {
+            appendClass(sb, "+++", jApiClass);
+        } else if (jApiClass.getChangeStatus() == JApiChangeStatus.REMOVED) {
+            appendClass(sb, "---", jApiClass);
+        } else if (jApiClass.getChangeStatus() == JApiChangeStatus.MODIFIED) {
+            appendClass(sb, "***", jApiClass);
+        }
+    }
+
+    private void appendMethod(StringBuilder sb, String signs, JApiMethod 
jApiMethod) {
+        sb.append("\t" + signs + " " + jApiMethod.getChangeStatus() + " METHOD 
" + jApiMethod.getName() + "(");
+        int paramCount = 0;
+        for (JApiParameter jApiParameter : jApiMethod.getParameters()) {
+            if (paramCount > 0) {
+                sb.append(",");
+            }
+            sb.append(jApiParameter.getType());
+            paramCount++;
+        }
+        sb.append(")\n");
+    }
+
+    private void appendClass(StringBuilder sb, String signs, JApiClass 
jApiClass) {
+        sb.append(signs + " " + jApiClass.getChangeStatus() + " " + 
jApiClass.getType() + " " + jApiClass.getFullyQualifiedName() + "\n");
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java 
b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java
new file mode 100644
index 0000000..939c6cb
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java
@@ -0,0 +1,24 @@
+package japicmp.output.xml;
+
+import japicmp.config.Options;
+import japicmp.model.JApiClass;
+import japicmp.output.OutputTransformer;
+import japicmp.output.xml.model.JApiCmpXmlRoot;
+
+import javax.xml.bind.JAXB;
+import java.io.File;
+import java.util.List;
+
+public class XmlOutputGenerator {
+
+    public void generate(File oldArchive, File newArchive, List<JApiClass> 
jApiClasses, Options options) {
+        JApiCmpXmlRoot jApiCmpXmlRoot = new JApiCmpXmlRoot();
+        jApiCmpXmlRoot.setOldJar(oldArchive.getAbsolutePath());
+        jApiCmpXmlRoot.setNewJar(newArchive.getAbsolutePath());
+        jApiCmpXmlRoot.setClasses(jApiClasses);
+        if (options.isOutputOnlyModifications()) {
+            OutputTransformer.removeUnchanged(jApiClasses);
+        }
+        JAXB.marshal(jApiCmpXmlRoot, new 
File(options.getXmlOutputFile().get()));
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/output/xml/model/JApiCmpXmlRoot.java 
b/japicmp/src/main/java/japicmp/output/xml/model/JApiCmpXmlRoot.java
new file mode 100644
index 0000000..77beafe
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/output/xml/model/JApiCmpXmlRoot.java
@@ -0,0 +1,43 @@
+package japicmp.output.xml.model;
+
+import japicmp.model.JApiClass;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.LinkedList;
+import java.util.List;
+
+@XmlRootElement(name = "japicmp")
+public class JApiCmpXmlRoot {
+    private String oldJar = "";
+    private String newJar = "";
+    private List<JApiClass> classes = new LinkedList<JApiClass>();
+
+    @XmlElement(name = "class")
+    public List<JApiClass> getClasses() {
+        return classes;
+    }
+
+    public void setClasses(List<JApiClass> classes) {
+        this.classes = classes;
+    }
+
+    @XmlAttribute
+    public String getNewJar() {
+        return newJar;
+    }
+
+    public void setNewJar(String newJar) {
+        this.newJar = newJar;
+    }
+
+    @XmlAttribute
+    public String getOldJar() {
+        return oldJar;
+    }
+
+    public void setOldJar(String oldJar) {
+        this.oldJar = oldJar;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/util/SignatureParser.java 
b/japicmp/src/main/java/japicmp/util/SignatureParser.java
new file mode 100644
index 0000000..9d4d96d
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/util/SignatureParser.java
@@ -0,0 +1,103 @@
+package japicmp.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class SignatureParser {
+    private List<String> parameters = new LinkedList<String>();
+    private String returnType = "void";
+
+    public void parse(String signature) {
+        int parenthesisCloseIndex = signature.indexOf(')');
+        if(parenthesisCloseIndex > -1) {
+            parseParameters(signature, parenthesisCloseIndex);
+            parseReturnValue(signature, parenthesisCloseIndex);
+        }
+    }
+
+    private void parseReturnValue(String signature, int parenthesisCloseIndex) 
{
+        String retValPart = signature.substring(parenthesisCloseIndex+1);
+        List<String> retValTypes = parseTypes(retValPart);
+        returnType = retValTypes.get(0);
+    }
+
+    private void parseParameters(String signature, int parenthesisCloseIndex) {
+        String paramPart = signature.substring(1, parenthesisCloseIndex);
+        List<String> paramTypes = parseTypes(paramPart);
+        parameters.clear();
+        parameters.addAll(paramTypes);
+    }
+
+    private List<String> parseTypes(String paramPart) {
+        List<String> types = new LinkedList<String>();
+        boolean arrayNotation = false;
+        for(int i=0; i<paramPart.length(); i++) {
+            char c = paramPart.charAt(i);
+            String type = "void";
+            switch(c) {
+                case 'Z':
+                    type = "boolean";
+                    break;
+                case 'B':
+                    type = "byte";
+                    break;
+                case 'C':
+                    type = "char";
+                    break;
+                case 'S':
+                    type = "short";
+                    break;
+                case 'I':
+                    type = "int";
+                    break;
+                case 'J':
+                    type = "long";
+                    break;
+                case 'F':
+                    type = "float";
+                    break;
+                case 'D':
+                    type = "double";
+                    break;
+                case 'V':
+                    type = "void";
+                    break;
+                case '[':
+                    arrayNotation = true;
+                    continue;
+                case 'L':
+                    StringBuilder fqn = new StringBuilder();
+                    i++;
+                    while(i<paramPart.length()) {
+                        c = paramPart.charAt(i);
+                        if(c == ';') {
+                            break;
+                        } else if(c == '/') {
+                            fqn.append('.');
+                        } else {
+                            fqn.append(c);
+                        }
+                        i++;
+                    }
+                    type = fqn.toString();
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown type signature: 
'"+c+"'");
+            }
+            if(arrayNotation) {
+                type += "[]";
+                arrayNotation = false;
+            }
+            types.add(type);
+        }
+        return types;
+    }
+
+    public List<String> getParameters() {
+        return parameters;
+    }
+
+    public String getReturnType() {
+        return returnType;
+    }
+}
diff --git a/japicmp/src/main/java/japicmp/util/StringArrayEnumeration.java 
b/japicmp/src/main/java/japicmp/util/StringArrayEnumeration.java
new file mode 100644
index 0000000..82c7eb6
--- /dev/null
+++ b/japicmp/src/main/java/japicmp/util/StringArrayEnumeration.java
@@ -0,0 +1,27 @@
+package japicmp.util;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+public class StringArrayEnumeration implements Enumeration<String> {
+    private final String[] array;
+    private int pos = 0;
+
+    public StringArrayEnumeration(String[] array) {
+        this.array = array;
+    }
+
+    @Override
+    public boolean hasMoreElements() {
+        return pos < array.length;
+    }
+
+    @Override
+    public String nextElement() {
+        if(hasMoreElements()) {
+            return array[pos++];
+        } else {
+            throw new NoSuchElementException();
+        }
+    }
+}
diff --git a/japicmp/src/main/resources/log4j.properties 
b/japicmp/src/main/resources/log4j.properties
new file mode 100644
index 0000000..71255bb
--- /dev/null
+++ b/japicmp/src/main/resources/log4j.properties
@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=DEBUG, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p 
%c{1}:%L - %m%n
\ No newline at end of file
diff --git a/japicmp/src/test/java/japicmp/cli/CliParserTest.java 
b/japicmp/src/test/java/japicmp/cli/CliParserTest.java
new file mode 100644
index 0000000..8dafe7f
--- /dev/null
+++ b/japicmp/src/test/java/japicmp/cli/CliParserTest.java
@@ -0,0 +1,40 @@
+package japicmp.cli;
+
+import japicmp.config.Options;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class CliParserTest {
+    private CliParser subject;
+
+    @Before
+    public void before() {
+        subject = new CliParser();
+    }
+
+    @Test
+    public void testAllOptions() {
+        Options options = subject.parse(new String[]{"-n", "npath", "-o", 
"opath", "-m", "-x", "xpath"});
+        assertThat(options.getXmlOutputFile().get(), is("xpath"));
+        assertThat(options.getNewArchive(), is("npath"));
+        assertThat(options.getOldArchive(), is("opath"));
+        assertThat(options.isOutputOnlyModifications(), is(true));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMissingArgumentForN() {
+        subject.parse(new String[]{"-n", "-o", "opath"});
+    }
+
+    @Test
+    public void testOnlyNAndO() {
+        Options options = subject.parse(new String[]{"-n", "npath", "-o", 
"opath",});
+        assertThat(options.getXmlOutputFile().isPresent(), is(false));
+        assertThat(options.getNewArchive(), is("npath"));
+        assertThat(options.getOldArchive(), is("opath"));
+        assertThat(options.isOutputOnlyModifications(), is(false));
+    }
+}
diff --git a/japicmp/src/test/java/japicmp/util/SignatureParserTest.java 
b/japicmp/src/test/java/japicmp/util/SignatureParserTest.java
new file mode 100644
index 0000000..183fd8a
--- /dev/null
+++ b/japicmp/src/test/java/japicmp/util/SignatureParserTest.java
@@ -0,0 +1,99 @@
+package japicmp.util;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class SignatureParserTest {
+    private SignatureParser subject;
+
+    @Before
+    public void before() throws Exception {
+        subject = new SignatureParser();
+    }
+
+    @Test
+    public void testNoParamsReturnsVoid() {
+        subject.parse("()V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(0));
+    }
+
+    @Test
+    public void testTwoReferenceParamsReturnsReference() {
+        
subject.parse("(Lorg/apache/http/conn/routing/HttpRoute;Ljava/lang/Object;)Lorg/apache/http/conn/ManagedClientConnection;");
+        assertThat(subject.getReturnType(), 
is("org.apache.http.conn.ManagedClientConnection"));
+        assertThat(subject.getParameters().size(), is(2));
+        assertThat(subject.getParameters(), 
hasItem("org.apache.http.conn.routing.HttpRoute"));
+        assertThat(subject.getParameters(), hasItem("java.lang.Object"));
+    }
+
+    @Test
+    public void testOneReferenceOnePrimParamsReturnsVoid() {
+        subject.parse("(JLjava/util/concurrent/TimeUnit;)V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(2));
+        assertThat(subject.getParameters(), hasItem("long"));
+        assertThat(subject.getParameters(), 
hasItem("java.util.concurrent.TimeUnit"));
+    }
+
+    @Test
+    public void testArrayTwoPrimParamsReturnsVoid() {
+        subject.parse("([BII)V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(3));
+        assertThat(subject.getParameters(), hasItem("byte[]"));
+        assertThat(subject.getParameters(), hasItem("int"));
+    }
+
+    @Test
+    public void testArrayPrimParamReturnsVoid() {
+        subject.parse("([B)V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(1));
+        assertThat(subject.getParameters(), hasItem("byte[]"));
+    }
+
+    @Test
+    public void testArrayRefParamReturnsVoid() {
+        subject.parse("([Lorg/apache/http/cookie/Cookie;)V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(1));
+        assertThat(subject.getParameters(), 
hasItem("org.apache.http.cookie.Cookie[]"));
+    }
+
+    @Test
+    public void testOneReferenceParamsReturnsVoid() {
+        subject.parse("(Lorg/apache/http/impl/conn/tsccm/BasicPoolEntry;)V");
+        assertThat(subject.getReturnType(), is("void"));
+        assertThat(subject.getParameters().size(), is(1));
+        assertThat(subject.getParameters(), 
hasItem("org.apache.http.impl.conn.tsccm.BasicPoolEntry"));
+    }
+
+    @Test
+    public void testOneReferenceParamsReturnsOneReference() {
+        subject.parse("(Ljava/util/List;)Ljava/util/List;");
+        assertThat(subject.getReturnType(), is("java.util.List"));
+        assertThat(subject.getParameters().size(), is(1));
+        assertThat(subject.getParameters(), hasItem("java.util.List"));
+    }
+
+    @Test
+    public void testNoParamsReturnsReference() {
+        subject.parse("()Lorg/apache/http/conn/scheme/SchemeRegistry;");
+        assertThat(subject.getReturnType(), 
is("org.apache.http.conn.scheme.SchemeRegistry"));
+        assertThat(subject.getParameters().size(), is(0));
+    }
+
+    @Test
+    public void testNoParamsReturnsI() {
+        subject.parse("()I");
+        assertThat(subject.getReturnType(), is("int"));
+        assertThat(subject.getParameters().size(), is(0));
+    }
+}
diff --git a/japicmp/src/test/java/japicmp/util/StringArrayEnumerationTest.java 
b/japicmp/src/test/java/japicmp/util/StringArrayEnumerationTest.java
new file mode 100644
index 0000000..853d71e
--- /dev/null
+++ b/japicmp/src/test/java/japicmp/util/StringArrayEnumerationTest.java
@@ -0,0 +1,37 @@
+package japicmp.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.NoSuchElementException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class StringArrayEnumerationTest {
+
+    @Test(expected = NoSuchElementException.class)
+    public void testEmptyArray() {
+        StringArrayEnumeration sae = new StringArrayEnumeration(new 
String[]{});
+        assertThat(sae.hasMoreElements(), is(false));
+        sae.nextElement();
+    }
+
+    @Test
+    public void testOneElementArray() {
+        StringArrayEnumeration sae = new StringArrayEnumeration(new 
String[]{"1"});
+        assertThat(sae.hasMoreElements(), is(true));
+        assertThat(sae.nextElement(), is("1"));
+        assertThat(sae.hasMoreElements(), is(false));
+    }
+
+    @Test
+    public void testTwoElementsArray() {
+        StringArrayEnumeration sae = new StringArrayEnumeration(new 
String[]{"1","2"});
+        assertThat(sae.hasMoreElements(), is(true));
+        assertThat(sae.nextElement(), is("1"));
+        assertThat(sae.hasMoreElements(), is(true));
+        assertThat(sae.nextElement(), is("2"));
+        assertThat(sae.hasMoreElements(), is(false));
+    }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a25f751
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>japicmp</groupId>
+    <artifactId>japicmp-base</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <modules>
+        <module>japicmp</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file

Reply via email to