From cb25c0fdda9aeebce496d82a36fc12bf399e7ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Hohwiller?= Date: Thu, 22 Aug 2019 15:44:04 +0200 Subject: [PATCH] #6: read source-code if no byte-code is available --- .../code/api/language/JavaLanguageTest.java | 6 ++- .../base/AbstractBaseContextWithCache.java | 28 ++++++++-- .../net/sf/mmm/code/base/type/BaseType.java | 26 ++++++++-- .../net/sf/mmm/code/base/TestContext.java | 3 +- .../sf/mmm/code/impl/java/JavaContext.java | 29 +++++++---- .../code/impl/java/JavaExtendedContext.java | 33 ++++++++++-- .../impl/java/loader/JavaSourceLoader.java | 19 ++++--- .../parser/JavaGenericTypeFromSource.java | 2 +- .../parser/JavaSourceCodeReaderHighlevel.java | 46 ++++++++++------ .../parser/JavaSourceCodeReaderLowlevel.java | 28 +++++++--- .../impl/java/JavaSourceCodeOnlyTest.java | 52 +++++++++++++++++++ .../sourcecode/com/example/demo/Demo.java | 27 ++++++++++ pom.xml | 3 +- 13 files changed, 238 insertions(+), 64 deletions(-) create mode 100644 java/impl/src/test/java/net/sf/mmm/code/impl/java/JavaSourceCodeOnlyTest.java create mode 100644 java/impl/src/test/resources/testdata/sourcecode/com/example/demo/Demo.java diff --git a/api/src/test/java/net/sf/mmm/code/api/language/JavaLanguageTest.java b/api/src/test/java/net/sf/mmm/code/api/language/JavaLanguageTest.java index 238937f..ae3eed8 100644 --- a/api/src/test/java/net/sf/mmm/code/api/language/JavaLanguageTest.java +++ b/api/src/test/java/net/sf/mmm/code/api/language/JavaLanguageTest.java @@ -86,8 +86,12 @@ public void testPackageNaming() { verifyPackageName(language, pkg, "a§b", false); verifyPackageName(language, pkg, "a௰", false); for (int nonAlphaNum = 0; nonAlphaNum < UNICODE_NON_ALPHANUMERIC_SYMBOLS.length(); nonAlphaNum++) { + if (nonAlphaNum == 2) { + continue; + } char c = UNICODE_NON_ALPHANUMERIC_SYMBOLS.charAt(nonAlphaNum); - assertThat(Character.isLetterOrDigit(c)).as("isLetterOrDigit(" + name[0] + ") expected to be false").isFalse(); + assertThat(Character.isLetterOrDigit(c)) + .as("isLetterOrDigit(" + c + ") [index " + nonAlphaNum + "] expected to be false").isFalse(); verifyPackageName(language, pkg, Character.toString(c), false); } } diff --git a/base/src/main/java/net/sf/mmm/code/base/AbstractBaseContextWithCache.java b/base/src/main/java/net/sf/mmm/code/base/AbstractBaseContextWithCache.java index d837a90..77ed6f3 100644 --- a/base/src/main/java/net/sf/mmm/code/base/AbstractBaseContextWithCache.java +++ b/base/src/main/java/net/sf/mmm/code/base/AbstractBaseContextWithCache.java @@ -10,6 +10,7 @@ import java.util.function.Supplier; import net.sf.mmm.code.api.CodeName; +import net.sf.mmm.code.api.type.CodeType; import net.sf.mmm.code.base.loader.BaseLoader; import net.sf.mmm.code.base.source.BaseSource; import net.sf.mmm.code.base.source.BaseSourceImpl; @@ -82,6 +83,18 @@ protected Map createCache() { */ protected abstract BaseLoader getLoader(); + @Override + public BaseType getOrCreateType(String qualifiedName, boolean add) { + + BaseType type = getType(qualifiedName); + if (type == null) { + BaseFile file = getSource().getRootPackage().getChildren().getOrCreateFile(parseName(qualifiedName), add); + type = file.getType(); + putTypeInCache(qualifiedName, type); + } + return type; + } + @Override public BaseType getType(String qualifiedName) { @@ -90,7 +103,7 @@ public BaseType getType(String qualifiedName) { return type; } type = getLoader().getType(qualifiedName); - return getTypeAndPutInCache(qualifiedName, type); + return putTypeInCache(qualifiedName, type); } @Override @@ -102,7 +115,7 @@ public BaseType getType(CodeName qName) { return type; } type = getLoader().getType(qName); - return getTypeAndPutInCache(qualifiedName, type); + return putTypeInCache(qualifiedName, type); } @Override @@ -118,7 +131,7 @@ public BaseGenericType getType(Class clazz) { return type; } type = getLoader().getType(clazz); - return getTypeAndPutInCache(qualifiedName, (BaseType) type); + return putTypeInCache(qualifiedName, (BaseType) type); } @Override @@ -134,10 +147,14 @@ protected BaseType getTypeFromCache(String qualifiedName) { return this.typeCache.get(qualifiedName); } - private BaseType getTypeAndPutInCache(String qualifiedName, BaseType type) { + private BaseType putTypeInCache(String qualifiedName, BaseType type) { if (type != null) { this.typeCache.put(qualifiedName, type); + // TODO prevent eager init...? + for (CodeType nested : type.getNestedTypes().getDeclared()) { + putTypeInCache(nested.getQualifiedName(), (BaseType) nested); + } } else { LOG.trace("Failed to get type {}", qualifiedName); } @@ -236,7 +253,8 @@ protected boolean isPreventRegisterSource() { private void verifyCreateSource(Object arg) { if (this.sourceProvider == null) { - throw new IllegalStateException("Can not create source for external code in " + getClass().getSimpleName() + ": " + arg); + throw new IllegalStateException( + "Can not create source for external code in " + getClass().getSimpleName() + ": " + arg); } } diff --git a/base/src/main/java/net/sf/mmm/code/base/type/BaseType.java b/base/src/main/java/net/sf/mmm/code/base/type/BaseType.java index bcea203..fc02f3a 100644 --- a/base/src/main/java/net/sf/mmm/code/base/type/BaseType.java +++ b/base/src/main/java/net/sf/mmm/code/base/type/BaseType.java @@ -185,6 +185,20 @@ public String getSimpleName() { return this.simpleName; } + /** + * @return the type name what is the {@link #getSimpleName() simple name} in case of a top-level type, and in case of + * a nested type the {@link #getTypeName() type name} of the {@link #getDeclaringType() declaring type} + * followed by a package separator and the {@link #getSimpleName() simple name}. + */ + protected String getTypeName() { + + String typeName = getSimpleName(); + if (this.declaringType != null) { + typeName = this.declaringType.getTypeName() + getLanguage().getPackageSeparator() + typeName; + } + return typeName; + } + @Override public void setSimpleName(String simpleName) { @@ -207,10 +221,11 @@ public String getQualifiedName() { } BasePackage pkg = getParentPackage(); String result; + String typeName = getTypeName(); if (pkg.isRoot()) { - result = getSimpleName(); + result = typeName; } else { - result = pkg.getQualifiedName() + getLanguage().getPackageSeparator() + getSimpleName(); + result = pkg.getQualifiedName() + getLanguage().getPackageSeparator() + typeName; } if (isImmutable()) { this.qualifiedName = result; @@ -469,8 +484,8 @@ public BaseType copy(CodeCopyMapper mapper) { } @Override - protected void doWrite(Appendable sink, String newline, String defaultIndent, String currentIndent, CodeLanguage language) - throws IOException { + protected void doWrite(Appendable sink, String newline, String defaultIndent, String currentIndent, + CodeLanguage language) throws IOException { if (defaultIndent == null) { writeReference(sink, true); @@ -481,7 +496,8 @@ protected void doWrite(Appendable sink, String newline, String defaultIndent, St doWriteBody(sink, newline, defaultIndent, currentIndent, language); } - void doWriteBody(Appendable sink, String newline, String defaultIndent, String currentIndent, CodeLanguage language) throws IOException { + void doWriteBody(Appendable sink, String newline, String defaultIndent, String currentIndent, CodeLanguage language) + throws IOException { sink.append(" {"); sink.append(newline); diff --git a/base/src/test/java/net/sf/mmm/code/base/TestContext.java b/base/src/test/java/net/sf/mmm/code/base/TestContext.java index 3e9ea9b..c19c1b3 100644 --- a/base/src/test/java/net/sf/mmm/code/base/TestContext.java +++ b/base/src/test/java/net/sf/mmm/code/base/TestContext.java @@ -167,7 +167,8 @@ public BaseGenericType getType(Class clazz) { Package pkg = clazz.getPackage(); if (pkg != null) { String pkgName = pkg.getName(); - BiFunction factory = (parentPkg, simpleName) -> createPackage(pkg, parentPkg, simpleName); + BiFunction factory = (parentPkg, simpleName) -> createPackage(pkg, parentPkg, + simpleName); parentPackage = getPackage(parentPackage.getChildren(), source.parseName(pkgName), false, factory, true, true); } BasePathElements children = parentPackage.getChildren(); diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaContext.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaContext.java index d3405a3..18f381c 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaContext.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaContext.java @@ -208,17 +208,28 @@ public ClassLoader getClassLoader() { @Override public BaseType getType(String qualifiedName) { - Class clazz = null; - try { - clazz = this.classLoader.loadClass(qualifiedName); - if (clazz.isArray()) { - throw new IllegalArgumentException(qualifiedName); + if (this.classLoader != null) { + Class clazz = null; + try { + clazz = this.classLoader.loadClass(qualifiedName); + if (clazz.isArray()) { + throw new IllegalArgumentException(qualifiedName); + } + return (BaseType) getContext().getType(clazz); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + if (LOG.isTraceEnabled()) { + LOG.debug("Class {} not found.", qualifiedName, e); + } else { + LOG.debug("Class {} not found: {}", qualifiedName, e.toString()); + } } - return (BaseType) getContext().getType(clazz); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - LOG.debug("Class {} not found.", qualifiedName, e); - return null; } + BaseType type = getSource().getLoader().getType(parseName(qualifiedName)); + if (type != null) { + // TODO make or create as system immutable to prevent eager init + type.setImmutable(); + } + return type; } @Override diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaExtendedContext.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaExtendedContext.java index e33729a..fc6cbae 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaExtendedContext.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/JavaExtendedContext.java @@ -16,7 +16,9 @@ public class JavaExtendedContext extends JavaContext { private final JavaContext parent; - private final JavaClassLoader loader; + private final BaseLoader loader; + + private final ClassLoader classLoader; /** * The constructor. @@ -38,17 +40,38 @@ public JavaExtendedContext(BaseSourceImpl source, BaseSourceProvider sourceProvi * @param sourceProvider the {@link BaseSourceProvider}. * @param classLoader the explicit {@link ClassLoader} used to load the byte-code. */ - public JavaExtendedContext(JavaContext parent, BaseSourceImpl source, BaseSourceProvider sourceProvider, ClassLoader classLoader) { + public JavaExtendedContext(JavaContext parent, BaseSourceImpl source, BaseSourceProvider sourceProvider, + ClassLoader classLoader) { super(source, sourceProvider); this.parent = parent; + JavaClassLoader jcl; if (classLoader == null) { - this.loader = new JavaClassLoader(); + jcl = new JavaClassLoader(); } else { - this.loader = new JavaClassLoader(classLoader); + jcl = new JavaClassLoader(classLoader); } + this.loader = jcl; + this.classLoader = jcl.getClassLoader(); + } + + /** + * The constructor. + * + * @param parent the {@link #getParent() parent context}. + * @param source the {@link #getSource() source}. + * @param sourceProvider the {@link BaseSourceProvider}. + */ + public JavaExtendedContext(JavaContext parent, BaseSourceImpl source, BaseSourceProvider sourceProvider) { + + super(source, sourceProvider); + + this.parent = parent; + + this.classLoader = null; + this.loader = new JavaClassLoader(null); } @Override @@ -72,7 +95,7 @@ public JavaRootContext getRootContext() { @Override public ClassLoader getClassLoader() { - return this.loader.getClassLoader(); + return this.classLoader; } } diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/loader/JavaSourceLoader.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/loader/JavaSourceLoader.java index 769fd0b..7d4b0d0 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/loader/JavaSourceLoader.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/loader/JavaSourceLoader.java @@ -103,7 +103,8 @@ public BaseType getType(CodeName qualifiedName) { return getTypeFromSource(parent, qualifiedName.getSimpleName()); } else { BasePackage pkg = getPackage(parent); - BaseFile file = getFileFromSource(pkg, qualifiedName.getSimpleName()); + BaseFile file = pkg.getChildren().createFile(qualifiedName.getSimpleName()); + getParser().parseType(reader, file); return file.getType(); } } catch (IOException e) { @@ -174,7 +175,8 @@ private BasePackage getPackage(CodeName qualifiedName) { private BasePackage createPackage(BasePackage parentPackage, String simpleName) { - BasePackage pkg = new BasePackage(parentPackage, simpleName, null, () -> getSourcePackage(parentPackage, simpleName), true); + BasePackage pkg = new BasePackage(parentPackage, simpleName, null, + () -> getSourcePackage(parentPackage, simpleName), true); return pkg; } @@ -222,16 +224,13 @@ private BaseType getTypeFromSource(CodeName parent, String simpleName) { return null; } String parentSimpleName = parent.getSimpleName(); - BaseType declaringType; if ((parentSimpleName.length() > 0) && Character.isUpperCase(parentSimpleName.charAt(0))) { - declaringType = getTypeFromSource(parent.getParent(), parentSimpleName); - } else { - declaringType = getType(parent); - } - if (declaringType == null) { - return null; + BaseType declaringType = getTypeFromSource(parent.getParent(), parentSimpleName); + if (declaringType != null) { + return (BaseType) declaringType.getNestedTypes().get(simpleName); + } } - return (BaseType) declaringType.getNestedTypes().get(simpleName); + return null; } @Override diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaGenericTypeFromSource.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaGenericTypeFromSource.java index d68e641..e743b55 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaGenericTypeFromSource.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaGenericTypeFromSource.java @@ -142,7 +142,7 @@ private CodeGenericType toGenericTypeByName(String typeName) { if (!qualified) { qualifiedName = context.getQualifiedName(typeName, this.file, false); } - BaseType baseType = context.getRequiredType(qualifiedName); + BaseType baseType = context.getOrCreateType(qualifiedName, false); if (qualified) { BaseTypeProxy qualifiedType = new BaseTypeProxy(this.parent, baseType); qualifiedType.setQualified(true); diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderHighlevel.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderHighlevel.java index ea36d1d..a1eb9cc 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderHighlevel.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderHighlevel.java @@ -107,7 +107,8 @@ private void parsePackage() { } String expectedPkg = this.file.getParentPackage().getQualifiedName(); if (!actualPkg.equals(expectedPkg)) { - LOG.warn("Expected package '{}' for file '{}' but found package '{}'", expectedPkg, this.file.getSimpleName(), actualPkg); + LOG.warn("Expected package '{}' for file '{}' but found package '{}'", expectedPkg, this.file.getSimpleName(), + actualPkg); } this.file.setComment(getElementComment()); @@ -156,8 +157,7 @@ private BaseType parseType(BaseType declaringType, CodeModifiers modifiers, Code } BaseType type = (BaseType) this.file.getType(simpleName, false); if (type == null) { - type = new BaseType(this.file, simpleName, declaringType, null); - this.file.getTypes().add(type); + type = (BaseType) declaringType.getNestedTypes().getDeclaredOrCreate(simpleName); } type.setCategory(category); type.setModifiers(modifiers); @@ -194,7 +194,8 @@ private void parseTypeBody(BaseType type) { } while (elementFound); if (!expect('}')) { String dummy = readUntil('}', true); - LOG.warn("Garbarge at the end of body in {} for type {}: {}", this.file.getQualifiedName(), type.getSimpleName(), dummy); + LOG.warn("Garbage at the end of body in {} for type {}: {}", this.file.getQualifiedName(), type.getSimpleName(), + dummy); } } @@ -300,7 +301,8 @@ private boolean parseTypeElement(BaseType type) { return parseTypeElementForMember(modifiers, memberComment, memberAnnotations, member); } - private boolean parseTypeElementForMember(CodeModifiers modifiers, CodeComment memberComment, List memberAnnotations, BaseMember member) { + private boolean parseTypeElementForMember(CodeModifiers modifiers, CodeComment memberComment, + List memberAnnotations, BaseMember member) { if (member != null) { member.setModifiers(modifiers); @@ -356,7 +358,8 @@ private void parseOperationArgs(BaseOperation operation) { } todo = !(expect(')')); if (todo && !expect(',')) { - LOG.warn("Expecting ',' or ')' to terminate signature of operation {} but found '{}' in {}", operation, "" + forcePeek(), this.file.getQualifiedName()); + LOG.warn("Expecting ',' or ')' to terminate signature of operation {} but found '{}' in {}", operation, + "" + forcePeek(), this.file.getQualifiedName()); } } } @@ -388,7 +391,8 @@ private void parseOperationBody(BaseOperation operation) { } List statements = parseBlock(); if (statements == null) { - LOG.warn("Expecting ';' or '{' to terminate signature of operation {} but found '{}' in {}", operation, "" + forcePeek(), this.file.getQualifiedName()); + LOG.warn("Expecting ';' or '{' to terminate signature of operation {} but found '{}' in {}", operation, + "" + forcePeek(), this.file.getQualifiedName()); return; } operation.setBody(new BaseBlockBody(operation, statements)); @@ -458,7 +462,8 @@ private JavaTypeVariablesFromSource parseTypeVariables(CodeElementWithTypeVariab todo = !expect('>'); if (todo) { if (!expect(',')) { - LOG.warn("Expecting '>' or ',' to terminate type parameter for {} at {} of {}", identifier, owner, this.file.getQualifiedName()); + LOG.warn("Expecting '>' or ',' to terminate type parameter for {} at {} of {}", identifier, owner, + this.file.getQualifiedName()); } } } @@ -467,7 +472,8 @@ private JavaTypeVariablesFromSource parseTypeVariables(CodeElementWithTypeVariab return result; } - private void parseTypeParameters(JavaGenericTypeFromSource type, CodeElementWithTypeVariables element, boolean withTypeParams) { + private void parseTypeParameters(JavaGenericTypeFromSource type, CodeElementWithTypeVariables element, + boolean withTypeParams) { if (expect('<')) { type.ensureTypeParameters(); @@ -480,7 +486,8 @@ private void parseTypeParameters(JavaGenericTypeFromSource type, CodeElementWith todo = !expect('>'); if (todo) { if (!expect(',')) { - LOG.warn("Expecting '>' or ',' to terminate type parameter for {} at {} of {}", type.getName(), element, this.file.getQualifiedName()); + LOG.warn("Expecting '>' or ',' to terminate type parameter for {} at {} of {}", type.getName(), element, + this.file.getQualifiedName()); } } } @@ -527,7 +534,8 @@ private void parseSuperTypes(BaseType type) { } } - private BaseGenericType parseGenericType(CodeElementWithTypeVariables element, boolean withTypeParams, boolean withArray, boolean withComposedTypes) { + private BaseGenericType parseGenericType(CodeElementWithTypeVariables element, boolean withTypeParams, + boolean withArray, boolean withComposedTypes) { String typeName = parseQName(); if (typeName == null) { @@ -540,8 +548,8 @@ private BaseGenericType parseGenericType(CodeElementWithTypeVariables element, b return parseGenericType(typeName, element, withTypeParams, withArray, withComposedTypes); } - private BaseGenericType parseGenericType(String typeName, CodeElementWithTypeVariables element, boolean withTypeParams, boolean withArray, - boolean withComposedTypes) { + private BaseGenericType parseGenericType(String typeName, CodeElementWithTypeVariables element, + boolean withTypeParams, boolean withArray, boolean withComposedTypes) { JavaGenericTypeFromSource type = new JavaGenericTypeFromSource(element, typeName, this.file); parseWhitespacesAndComments(); @@ -591,7 +599,8 @@ private boolean parseBound(BaseTypeVariable typeVariable, CodeElementWithTypeVar requireWhitespace(element, keyword, name); BaseGenericType bound = parseGenericType(element, true, true, true); if (bound == null) { - LOG.warn("Missing {} bound after type parameter {} at {} of {}", keyword, name, element, this.file.getQualifiedName()); + LOG.warn("Missing {} bound after type parameter {} at {} of {}", keyword, name, element, + this.file.getQualifiedName()); return false; } typeVariable.setBound(bound); @@ -610,7 +619,8 @@ private void requireWhitespace(CodeElement element, String keyword, Object conte } } - private boolean parseBound(JavaGenericTypeFromSource type, boolean superBound, CodeElementWithTypeVariables element, boolean withComposedTypes) { + private boolean parseBound(JavaGenericTypeFromSource type, boolean superBound, CodeElementWithTypeVariables element, + boolean withComposedTypes) { String keyword; if (superBound) { @@ -621,11 +631,13 @@ private boolean parseBound(JavaGenericTypeFromSource type, boolean superBound, C if (expectStrict(keyword)) { int count = skipWhile(CharFilter.WHITESPACE_FILTER); if (count == 0) { - LOG.warn("Missing whitespace after {} expression of type parameter {} at {} of {}", keyword, type.getName(), element, this.file.getQualifiedName()); + LOG.warn("Missing whitespace after {} expression of type parameter {} at {} of {}", keyword, type.getName(), + element, this.file.getQualifiedName()); } BaseGenericType bound = parseGenericType(element, true, true, withComposedTypes); if (bound == null) { - LOG.warn("Missing {} bound after type parameter {} at {} of {}", keyword, type.getName(), element, this.file.getQualifiedName()); + LOG.warn("Missing {} bound after type parameter {} at {} of {}", keyword, type.getName(), element, + this.file.getQualifiedName()); return false; } if (superBound) { diff --git a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderLowlevel.java b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderLowlevel.java index 6a75383..8e217e2 100644 --- a/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderLowlevel.java +++ b/java/impl/src/main/java/net/sf/mmm/code/impl/java/parser/JavaSourceCodeReaderLowlevel.java @@ -62,10 +62,11 @@ public abstract class JavaSourceCodeReaderLowlevel extends CharReaderScanner { static final CharFilter CHAR_FILTER_ANNOTATION_KEY = c -> ((c == '{') || (c == '=') || (c == ',')); - static final CharFilter CHAR_FILTER_OPERATOR = c -> ((c == '+') || (c == '-') || (c == '*') || (c == '/') || (c == '^') || (c == '%') || (c == '>') - || (c == '<') || (c == '!') || (c == '~') || (c == '=')); + static final CharFilter CHAR_FILTER_OPERATOR = c -> ((c == '+') || (c == '-') || (c == '*') || (c == '/') + || (c == '^') || (c == '%') || (c == '>') || (c == '<') || (c == '!') || (c == '~') || (c == '=')); - static final CharFilter CHAR_FILTER_NUMBER_LITERAL_START = c -> ((c >= '0') && (c <= '9') || (c == '+') || (c == '-')); + static final CharFilter CHAR_FILTER_NUMBER_LITERAL_START = c -> ((c >= '0') && (c <= '9') || (c == '+') + || (c == '-')); static final CharFilter CHAR_FILTER_STATEMENT_END = c -> ((c == ';') || (c == '\r') || (c == '\n')); @@ -274,7 +275,8 @@ private void parseAnnotationParameters(CodeAnnotation annotation, String annotat if (first) { key = "value"; // Java build in default } else { - LOG.warn("Annotation {} parameter without name (found '{}') in {}.", annotationTypeName, Character.toString(forcePeek()), this.file); + LOG.warn("Annotation {} parameter without name (found '{}') in {}.", annotationTypeName, + Character.toString(forcePeek()), this.file); } } else { parseWhitespacesAndComments(); @@ -295,8 +297,8 @@ private void parseAnnotationParameters(CodeAnnotation annotation, String annotat } } if (value == null) { - LOG.warn("Invalid character '{}' after annotation parameter {}.{} name in {}.", Character.toString(forcePeek()), annotationTypeName, key, - this.file); + LOG.warn("Invalid character '{}' after annotation parameter {}.{} name in {}.", + Character.toString(forcePeek()), annotationTypeName, key, this.file); } } } @@ -311,7 +313,8 @@ private void parseAnnotationParameters(CodeAnnotation annotation, String annotat } } while ((arg != null) && expect(',')); if (!expect('}')) { - LOG.warn("Invalid annotation array value - missing closing curly brace '}' for annotation {} at value {}", annotationTypeName, key); + LOG.warn("Invalid annotation array value - missing closing curly brace '}' for annotation {} at value {}", + annotationTypeName, key); } value = new BaseArrayInstatiation(args); } else { @@ -531,7 +534,7 @@ private String readDocOrCommentLine() { */ protected CodeModifiers parseModifiers(boolean inInterface) { - CodeVisibility visibility = parseVisibility(getVisibilityFallback(inInterface)); + CodeVisibility visibility = parseVisibility(null); Set modifiers = new HashSet<>(); boolean found = true; while (found) { @@ -560,6 +563,15 @@ protected CodeModifiers parseModifiers(boolean inInterface) { } else { found = false; } + if (visibility == null) { + visibility = parseVisibility(null); + if (!found) { + found = (visibility != null); + } + } + } + if (visibility == null) { + visibility = getVisibilityFallback(inInterface); } return new CodeModifiers(visibility, modifiers); } diff --git a/java/impl/src/test/java/net/sf/mmm/code/impl/java/JavaSourceCodeOnlyTest.java b/java/impl/src/test/java/net/sf/mmm/code/impl/java/JavaSourceCodeOnlyTest.java new file mode 100644 index 0000000..1dea3c5 --- /dev/null +++ b/java/impl/src/test/java/net/sf/mmm/code/impl/java/JavaSourceCodeOnlyTest.java @@ -0,0 +1,52 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package net.sf.mmm.code.impl.java; + +import java.io.File; +import java.util.List; + +import net.sf.mmm.code.api.source.CodeSourceDescriptor; +import net.sf.mmm.code.api.type.CodeType; +import net.sf.mmm.code.base.loader.BaseSourceCodeProviderDirectory; +import net.sf.mmm.code.base.loader.BaseSourceLoader; +import net.sf.mmm.code.base.loader.SourceCodeProvider; +import net.sf.mmm.code.base.source.BaseSourceDescriptorType; +import net.sf.mmm.code.base.source.BaseSourceImpl; +import net.sf.mmm.code.base.source.BaseSourceProvider; +import net.sf.mmm.code.base.type.BaseType; +import net.sf.mmm.code.impl.java.loader.JavaSourceLoader; + +import org.junit.Test; + +/** + * Test of {@link JavaExtendedContext} with only source code available. + */ +public class JavaSourceCodeOnlyTest extends AbstractBaseTypeTest { + + /** Parse Demo.java where only source-code is available. */ + @Test + public void testSourceCodeOnly() { + + File sourceLocation = new File("src/test/resources/testdata/sourcecode"); + SourceCodeProvider sourceCodeProvider = new BaseSourceCodeProviderDirectory(sourceLocation); + BaseSourceLoader loader = new JavaSourceLoader(sourceCodeProvider); + String id = "com.example.demo"; + CodeSourceDescriptor descriptor = new BaseSourceDescriptorType(id); + BaseSourceImpl source = new BaseSourceImpl(null, sourceLocation, id, descriptor, loader); + BaseSourceProvider sourceProvider = null; + JavaExtendedContext context = new JavaExtendedContext(JavaRootContext.get(), source, sourceProvider); + BaseType type = context.getType("com.example.demo.Demo"); + assertThat(type.getQualifiedName().equals("com.example.demo.Demo")); + assertThat(type.getDoc().getLines()) + .containsExactlyInAnyOrder("This is a simple java file to test reading of source-code only Java context."); + assertThat(type.getConstructors().getDeclared()).hasSize(1); + assertThat(type.getMethods().getDeclared()).hasSize(2); + List nestedTypes = type.getNestedTypes().getDeclared(); + assertThat(nestedTypes).hasSize(1); + CodeType nestedType = nestedTypes.get(0); + assertThat(nestedType.getQualifiedName()).isEqualTo("com.example.demo.Demo.Foo"); + assertThat(context.getType("com.example.demo.Demo.Foo")).isSameAs(nestedType); + context.close(); + } + +} diff --git a/java/impl/src/test/resources/testdata/sourcecode/com/example/demo/Demo.java b/java/impl/src/test/resources/testdata/sourcecode/com/example/demo/Demo.java new file mode 100644 index 0000000..40aebab --- /dev/null +++ b/java/impl/src/test/resources/testdata/sourcecode/com/example/demo/Demo.java @@ -0,0 +1,27 @@ +package com.example.demo; + +/** + * This is a simple java file to test reading of source-code only Java context. + */ +public class Demo { + + private String name; + + public Demo() { + + } + + public String getName() { + + return this.name; + } + + public void setName(String name) { + + this.name = name; + } + + private static class Foo { + + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6f17fa7..6c981ad 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,6 @@ 2.7.1 ../parent/pom.xml - net.sf.m-m-m mmm-code-parent dev-SNAPSHOT pom @@ -21,7 +20,7 @@ - dev + 1.0.0-beta3 -SNAPSHOT 7.5.1 1.8