From 9811fe796d6f549151ab904714e1a65ff2839ee2 Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Sun, 7 Jul 2013 23:01:07 +0300 Subject: [PATCH] java syntax parser --- .../java/methods/ClassReference.java | 48 ++++++++++++ .../inspector/java/methods/Clazz.java | 73 +++++++++++++++++++ .../java/methods/EnumerationBuffer.java | 40 ++++++++++ .../inspector/java/methods/JavaFile.java | 73 +++++++++++++++---- .../inspector/java/methods/Modifiers.java | 68 +++++++++++++++++ .../inspector/java/methods/Package.java | 10 +++ .../inspector/java/methods/Project.java | 51 +------------ .../java/methods/ProjectScanner.java | 63 ++++++++++++++++ .../tokenizer/InvalidSyntaxException.java | 11 +++ .../inspector/tokenizer/Tokenizer.java | 28 +++++++ 10 files changed, 402 insertions(+), 63 deletions(-) create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/ClassReference.java create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/Clazz.java create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/EnumerationBuffer.java create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/Modifiers.java create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/Package.java create mode 100644 src/main/java/eu/svjatoslav/inspector/java/methods/ProjectScanner.java create mode 100644 src/main/java/eu/svjatoslav/inspector/tokenizer/InvalidSyntaxException.java diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/ClassReference.java b/src/main/java/eu/svjatoslav/inspector/java/methods/ClassReference.java new file mode 100644 index 0000000..43d7625 --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/ClassReference.java @@ -0,0 +1,48 @@ +package eu.svjatoslav.inspector.java.methods; + +import java.util.ArrayList; +import java.util.List; + +import eu.svjatoslav.inspector.tokenizer.InvalidSyntaxException; +import eu.svjatoslav.inspector.tokenizer.Tokenizer; + +public class ClassReference { + + public String name; + + List typeParameters = new ArrayList(); + + public ClassReference(final Tokenizer tokenizer) + throws InvalidSyntaxException { + name = tokenizer.getToken().token; + + if (!tokenizer.isNextToken("<")) + return; + + while (true) { + final ClassReference parameterType = new ClassReference(tokenizer); + typeParameters.add(parameterType); + + if (!tokenizer.isNextToken(",")) + break; + } + + tokenizer.expectToken(">"); + } + + @Override + public String toString() { + final EnumerationBuffer result = new EnumerationBuffer(); + + result.append(name); + + if (typeParameters.size() > 0) { + result.append("<"); + for (final ClassReference classReference : typeParameters) + result.appendEnumeration(classReference.toString()); + result.append(">"); + } + + return result.toString(); + } +} diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/Clazz.java b/src/main/java/eu/svjatoslav/inspector/java/methods/Clazz.java new file mode 100644 index 0000000..fda73d1 --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/Clazz.java @@ -0,0 +1,73 @@ +package eu.svjatoslav.inspector.java.methods; + +import java.util.ArrayList; +import java.util.List; + +import eu.svjatoslav.inspector.tokenizer.InvalidSyntaxException; +import eu.svjatoslav.inspector.tokenizer.Tokenizer; +import eu.svjatoslav.inspector.tokenizer.TokenizerMatch; + +public class Clazz { + + private final String packageName; + private final String className; + + public ClassReference superClass; + public List implementedInterfaces = new ArrayList(); + + public Clazz(final String packageName, final String className, + final Tokenizer tokenizer) throws InvalidSyntaxException { + this.packageName = packageName; + this.className = className; + + while (true) { + final TokenizerMatch match = tokenizer.getToken(); + + if ("extends".equals(match.token)) { + superClass = new ClassReference(tokenizer); + continue; + } + + if ("implements".equals(match.token)) { + while (true) { + implementedInterfaces.add(new ClassReference(tokenizer)); + + if (tokenizer.isNextToken(",")) + continue; + + break; + } + continue; + } + + if ("{".equals(match.token)) { + parseClassBody(tokenizer); + break; + } + + } + } + + public void parseClassBody(final Tokenizer tokenizer) { + tokenizer.skipUtilEnd(); + } + + @Override + public String toString() { + final EnumerationBuffer result = new EnumerationBuffer(); + + result.append(packageName + " -> " + className + "\n"); + if (superClass != null) + result.append(" super: " + superClass.toString() + "\n"); + + if (implementedInterfaces.size() > 0) { + result.append(" implements: "); + for (final ClassReference classReference : implementedInterfaces) + result.appendEnumeration(classReference.toString()); + result.append("\n"); + } + + return result.toString(); + } + +} diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/EnumerationBuffer.java b/src/main/java/eu/svjatoslav/inspector/java/methods/EnumerationBuffer.java new file mode 100644 index 0000000..0dcf853 --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/EnumerationBuffer.java @@ -0,0 +1,40 @@ +package eu.svjatoslav.inspector.java.methods; + +public class EnumerationBuffer { + + private final String enumerationDelimiter; + + private final StringBuffer buffer = new StringBuffer(); + + public int enumeratedEntitiesCount = 0; + + public EnumerationBuffer() { + this(", "); + } + + public EnumerationBuffer(final String enumerationDelimiter) { + this.enumerationDelimiter = enumerationDelimiter; + } + + public void append(final String value) { + buffer.append(value); + } + + public void appendEnumeration(final String value) { + if (enumeratedEntitiesCount > 0) + buffer.append(enumerationDelimiter); + + buffer.append(value); + enumeratedEntitiesCount++; + } + + public void resetEnumeration() { + enumeratedEntitiesCount = 0; + } + + @Override + public String toString() { + return buffer.toString(); + } + +} diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/JavaFile.java b/src/main/java/eu/svjatoslav/inspector/java/methods/JavaFile.java index 5b636d6..400ff87 100644 --- a/src/main/java/eu/svjatoslav/inspector/java/methods/JavaFile.java +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/JavaFile.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import eu.svjatoslav.inspector.tokenizer.InvalidSyntaxException; import eu.svjatoslav.inspector.tokenizer.Tokenizer; import eu.svjatoslav.inspector.tokenizer.TokenizerMatch; @@ -15,16 +16,18 @@ public class JavaFile { private final List imports = new ArrayList(); + private String packageName; + private final File file; StringBuffer contents = new StringBuffer(); - public JavaFile(final File file) throws IOException { + public JavaFile(final File file) throws IOException, InvalidSyntaxException { this.file = file; parse(); } - public void parse() throws IOException { + public void parse() throws IOException, InvalidSyntaxException { System.out.println("java file: " + file); readFile(); @@ -41,26 +44,59 @@ public class JavaFile { tokenizer.addTerminator(")", false); tokenizer.addTerminator("[", false); tokenizer.addTerminator("]", false); + tokenizer.addTerminator("<", false); + tokenizer.addTerminator(">", false); + tokenizer.addTerminator(",", false); + + final Modifiers modifiers = new Modifiers(); while (true) { final TokenizerMatch match = tokenizer.getToken(); if (match == null) break; - if (match.token.equals("import")) - parseImport(tokenizer); - - if (match.token.equals("package")) + if (match.token.equals("package")) { parsePackage(tokenizer); + continue; + } + if (match.token.equals("import")) { + parseImport(tokenizer); + continue; + } + + final boolean wasModifier = modifiers.parseModifier(match.token); + if (wasModifier) + continue; + + if ("class".equals(match.token)) { + parseClass(tokenizer); + continue; + } + + System.out.println(" " + modifiers.toString() + " " + + match.token); + modifiers.reset(); + skipUntilSemicolon(tokenizer); } } - private void parseImport(final Tokenizer tokenizer) { + private void parseClass(final Tokenizer tokenizer) + throws InvalidSyntaxException { + + final TokenizerMatch match = tokenizer.getToken(); + final Clazz clazz = new Clazz(packageName, match.token, tokenizer); + System.out.println(clazz.toString()); + + } + + private void parseImport(final Tokenizer tokenizer) + throws InvalidSyntaxException { + final Import imp = new Import(); - TokenizerMatch match = tokenizer.getToken(); + final TokenizerMatch match = tokenizer.getToken(); if (match.token.equals("static")) { imp.isStatic = true; @@ -70,18 +106,17 @@ public class JavaFile { imports.add(imp); - // ; - match = tokenizer.getToken(); + tokenizer.expectToken(";"); } - private void parsePackage(final Tokenizer tokenizer) { + private void parsePackage(final Tokenizer tokenizer) + throws InvalidSyntaxException { - TokenizerMatch match = tokenizer.getToken(); + final TokenizerMatch match = tokenizer.getToken(); - System.out.println(match.token); + packageName = match.token; - // ; - match = tokenizer.getToken(); + tokenizer.expectToken(";"); } private void readFile() throws FileNotFoundException, IOException { @@ -103,4 +138,12 @@ public class JavaFile { fileReader.close(); } + public void skipUntilSemicolon(final Tokenizer tokenizer) { + while (true) { + final TokenizerMatch token = tokenizer.getToken(); + if (token.token.equals(";")) + return; + } + } + } diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/Modifiers.java b/src/main/java/eu/svjatoslav/inspector/java/methods/Modifiers.java new file mode 100644 index 0000000..747d7db --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/Modifiers.java @@ -0,0 +1,68 @@ +package eu.svjatoslav.inspector.java.methods; + +public class Modifiers { + + public enum Access { + PUBLIC("public"), PROTECTED("protected"), DEFAULT(""), PRIVATE( + "private"); + + public final String name; + + Access(final String name) { + this.name = name; + }; + } + + Access access = Access.DEFAULT; + + boolean isStatic = false;; + + boolean isFinal = false; + + public boolean parseModifier(final String string) { + for (final Access access : Access.values()) + if (access.name.equals(string)) { + this.access = access; + return true; + } + + if ("static".equals(string)) { + isStatic = true; + return true; + } + + if ("final".equals(string)) { + isFinal = true; + return true; + } + + return false; + } + + public void reset() { + isStatic = false; + isFinal = false; + access = Access.DEFAULT; + } + + @Override + public String toString() { + final StringBuffer result = new StringBuffer(); + + result.append(access.name); + + if (isStatic) { + if (result.length() > 0) + result.append(" "); + result.append("static"); + } + + if (isFinal) { + if (result.length() > 0) + result.append(" "); + result.append("final"); + } + + return result.toString(); + } +} diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/Package.java b/src/main/java/eu/svjatoslav/inspector/java/methods/Package.java new file mode 100644 index 0000000..9a98a7d --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/Package.java @@ -0,0 +1,10 @@ +package eu.svjatoslav.inspector.java.methods; + +import java.util.HashMap; +import java.util.Map; + +public class Package { + + Map classes = new HashMap(); + +} diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/Project.java b/src/main/java/eu/svjatoslav/inspector/java/methods/Project.java index 2688e78..b3a87cc 100644 --- a/src/main/java/eu/svjatoslav/inspector/java/methods/Project.java +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/Project.java @@ -1,55 +1,10 @@ package eu.svjatoslav.inspector.java.methods; -import java.io.File; -import java.io.IOException; - -import eu.svjatoslav.commons.file.FilePathParser; +import java.util.HashMap; +import java.util.Map; public class Project { - private final File projectPath; - - public Project(final File projectPath) { - this.projectPath = projectPath; - parse(); - } - - public void parse() { - - if (!projectPath.exists()) - System.out.println("Project not found on path: " + projectPath); - - if (!projectPath.canRead()) - System.out.println("Cannot read project path: " + projectPath); - - if (projectPath.isDirectory()) - parseDirectory(projectPath); - - if (projectPath.isFile()) - parseFile(projectPath); - } - - public void parseDirectory(final File file) { - - for (final File subFile : file.listFiles()) { - - if (subFile.isFile()) - parseFile(subFile); - - if (subFile.isDirectory()) - parseDirectory(subFile); - } - } - - public void parseFile(final File file) { - final String fileExtension = FilePathParser.getFileExtension(file); - if ("java".equalsIgnoreCase(fileExtension)) - try { - final JavaFile javaFile = new JavaFile(file); - } catch (final IOException e) { - System.out.println("Error parsing file: " + file.toString() - + ", " + e.toString()); - } - } + Map packages = new HashMap(); } diff --git a/src/main/java/eu/svjatoslav/inspector/java/methods/ProjectScanner.java b/src/main/java/eu/svjatoslav/inspector/java/methods/ProjectScanner.java new file mode 100644 index 0000000..3ed5a31 --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/java/methods/ProjectScanner.java @@ -0,0 +1,63 @@ +package eu.svjatoslav.inspector.java.methods; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import eu.svjatoslav.commons.file.FilePathParser; +import eu.svjatoslav.inspector.tokenizer.InvalidSyntaxException; + +public class ProjectScanner { + + private final File scanPath; + + Map projects = new HashMap(); + + public ProjectScanner(final File projectPath) { + scanPath = projectPath; + parse(); + } + + public void parse() { + + if (!scanPath.exists()) + System.out.println("Path not found: " + scanPath); + + if (!scanPath.canRead()) + System.out.println("Cannot read path: " + scanPath); + + if (scanPath.isDirectory()) + parseDirectory(scanPath); + + if (scanPath.isFile()) + parseFile(scanPath); + } + + public void parseDirectory(final File file) { + + for (final File subFile : file.listFiles()) { + + if (subFile.isFile()) + parseFile(subFile); + + if (subFile.isDirectory()) + parseDirectory(subFile); + } + } + + public void parseFile(final File file) { + final String fileExtension = FilePathParser.getFileExtension(file); + if ("java".equalsIgnoreCase(fileExtension)) + try { + final JavaFile javaFile = new JavaFile(file); + } catch (final IOException e) { + System.out.println("Error parsing file: " + file.toString() + + ": " + e.toString()); + } catch (final InvalidSyntaxException e) { + System.out.println("Syntax error occured while parsing file: " + + file.toString() + ": " + e.toString()); + } + } + +} diff --git a/src/main/java/eu/svjatoslav/inspector/tokenizer/InvalidSyntaxException.java b/src/main/java/eu/svjatoslav/inspector/tokenizer/InvalidSyntaxException.java new file mode 100644 index 0000000..1d47f4c --- /dev/null +++ b/src/main/java/eu/svjatoslav/inspector/tokenizer/InvalidSyntaxException.java @@ -0,0 +1,11 @@ +package eu.svjatoslav.inspector.tokenizer; + +public class InvalidSyntaxException extends Exception { + + private static final long serialVersionUID = 88294980027680555L; + + public InvalidSyntaxException(final String cause) { + super(cause); + } + +} diff --git a/src/main/java/eu/svjatoslav/inspector/tokenizer/Tokenizer.java b/src/main/java/eu/svjatoslav/inspector/tokenizer/Tokenizer.java index 97f1224..8a42776 100644 --- a/src/main/java/eu/svjatoslav/inspector/tokenizer/Tokenizer.java +++ b/src/main/java/eu/svjatoslav/inspector/tokenizer/Tokenizer.java @@ -2,12 +2,15 @@ package eu.svjatoslav.inspector.tokenizer; import java.util.ArrayList; import java.util.List; +import java.util.Stack; public class Tokenizer { private final List terminators = new ArrayList(); private final String source; + Stack tokenIndexes = new Stack(); + private int currentIndex = 0; public Tokenizer(final String source) { @@ -19,7 +22,15 @@ public class Tokenizer { terminators.add(new Terminator(terminator, empty)); } + public void expectToken(final String value) throws InvalidSyntaxException { + final TokenizerMatch match = getToken(); + if (!value.equals(match.token)) + throw new InvalidSyntaxException("Expected \"" + value + + "\" but got \"" + match.token + "\" instead."); + } + public TokenizerMatch getToken() { + tokenIndexes.push(currentIndex); final StringBuffer result = new StringBuffer(); while (true) { @@ -55,6 +66,23 @@ public class Tokenizer { } + public boolean isNextToken(final String token) { + if (token.equals(getToken().token)) + return true; + + rollbackToken(); + return false; + } + + public void rollbackToken() { + currentIndex = tokenIndexes.pop(); + } + + public void skipUtilEnd() { + tokenIndexes.push(currentIndex); + currentIndex = source.length(); + } + public boolean terminatorMatches(final Terminator terminator) { if ((currentIndex + terminator.value.length()) > source.length()) return false; -- 2.20.1