diff --git a/src/main/java/com/ibm/cldk/SymbolTable.java b/src/main/java/com/ibm/cldk/SymbolTable.java index d09ae85a..4caf5e9a 100644 --- a/src/main/java/com/ibm/cldk/SymbolTable.java +++ b/src/main/java/com/ibm/cldk/SymbolTable.java @@ -11,6 +11,7 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.ReferenceType; import com.github.javaparser.ast.type.Type; +import com.github.javaparser.printer.DefaultPrettyPrinter; import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; @@ -554,7 +555,15 @@ private static Pair processCallableDeclaration(CallableDeclara callableNode.setStartLine(callableDecl.getRange().isPresent() ? callableDecl.getRange().get().begin.line : -1); callableNode.setEndLine(callableDecl.getRange().isPresent() ? callableDecl.getRange().get().end.line : -1); callableNode.setReferencedTypes(getReferencedTypes(body)); - callableNode.setCode(body.isPresent() ? LexicalPreservingPrinter.print(body.get()) : ""); + try { + callableNode.setCode(body.isPresent() ? LexicalPreservingPrinter.print(body.get()) : ""); + } catch (UnsupportedOperationException uoe) { + Log.warn("LexicalPreservingPrinter.print() failed on method " + callableDecl.getSignature() + + " of type "+typeName); + Log.warn("Reverting to default printing"); + Log.warn(body.get().toString()); + callableNode.setCode(body.get().toString()); + } callableNode.setCodeStartLine(body.isPresent()? body.get().getBegin().get().line : -1); callableNode.setAccessedFields(getAccessedFields(body, classFields, typeName)); diff --git a/src/test/java/com/ibm/cldk/SymbolTableTest.java b/src/test/java/com/ibm/cldk/SymbolTableTest.java index 826443a7..1eb327bc 100644 --- a/src/test/java/com/ibm/cldk/SymbolTableTest.java +++ b/src/test/java/com/ibm/cldk/SymbolTableTest.java @@ -56,6 +56,17 @@ public void testExtractSingleMissingNodeRange() throws IOException { Assertions.assertEquals(2, typeDeclaration.size()); } + @Test + public void testExtractSingleDefaultKeywordMethodDecl() throws IOException { + String javaCode = getJavaCodeForTestResource("test-applications/default-keyword-method-decl/IndexExtractor.java"); + Map symbolTable = SymbolTable.extractSingle(javaCode).getLeft(); + Assertions.assertEquals(1, symbolTable.size()); + Map typeDeclaration = symbolTable.values().iterator().next().getTypeDeclarations(); + Assertions.assertEquals(1, typeDeclaration.size()); + Map callables = typeDeclaration.values().iterator().next().getCallableDeclarations(); + Assertions.assertEquals(5, callables.size()); + } + @Test public void testCallSiteArgumentExpression() throws IOException { String javaCode = getJavaCodeForTestResource("test-applications/generics-varargs-duplicate-signature-test/Validate.java"); diff --git a/src/test/resources/test-applications/default-keyword-method-decl/IndexExtractor.java b/src/test/resources/test-applications/default-keyword-method-decl/IndexExtractor.java new file mode 100644 index 00000000..1df739c9 --- /dev/null +++ b/src/test/resources/test-applications/default-keyword-method-decl/IndexExtractor.java @@ -0,0 +1,160 @@ +import java.util.Arrays; +import java.util.BitSet; +import java.util.Objects; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; + +/** + * An object that produces indices of a Bloom filter. + *

+ * The default implementation of {@code asIndexArray} is slow. Implementers should reimplement the + * method where possible.

+ * + * @since 4.5.0-M2 + */ +@FunctionalInterface +public interface IndexExtractor { + + /** + * Creates an IndexExtractor from a {@code BitMapExtractor}. + * + * @param bitMapExtractor the {@code BitMapExtractor} + * @return a new {@code IndexExtractor}. + */ + static IndexExtractor fromBitMapExtractor(final BitMapExtractor bitMapExtractor) { + Objects.requireNonNull(bitMapExtractor, "bitMapExtractor"); + return consumer -> { + final LongPredicate longPredicate = new LongPredicate() { + int wordIdx; + + @Override + public boolean test(long word) { + int i = wordIdx; + while (word != 0) { + if ((word & 1) == 1 && !consumer.test(i)) { + return false; + } + word >>>= 1; + i++; + } + wordIdx += 64; + return true; + } + }; + return bitMapExtractor.processBitMaps(longPredicate::test); + }; + } + + /** + * Creates an IndexExtractor from an array of integers. + * + * @param values the index values + * @return an IndexExtractor that uses the values. + */ + static IndexExtractor fromIndexArray(final int... values) { + return new IndexExtractor() { + + @Override + public int[] asIndexArray() { + return values.clone(); + } + + @Override + public boolean processIndices(final IntPredicate predicate) { + for (final int value : values) { + if (!predicate.test(value)) { + return false; + } + } + return true; + } + }; + } + + /** + * Return a copy of the IndexExtractor data as an int array. + * + *

Indices ordering and uniqueness is not guaranteed.

+ * + *

+ * The default implementation of this method creates an array and populates + * it. Implementations that have access to an index array should consider + * returning a copy of that array if possible. + *

+ * + * @return An int array of the data. + */ + default int[] asIndexArray() { + final class Indices { + private int[] data = new int[32]; + private int size; + + boolean add(final int index) { + data = IndexUtils.ensureCapacityForAdd(data, size); + data[size++] = index; + return true; + } + + int[] toArray() { + // Edge case to avoid a large array copy + return size == data.length ? data : Arrays.copyOf(data, size); + } + } + final Indices indices = new Indices(); + processIndices(indices::add); + return indices.toArray(); + } + + /** + * Each index is passed to the predicate. The predicate is applied to each + * index value, if the predicate returns {@code false} the execution is stopped, {@code false} + * is returned, and no further indices are processed. + * + *

Any exceptions thrown by the action are relayed to the caller.

+ * + *

Indices ordering and uniqueness is not guaranteed.

+ * + * @param predicate the action to be performed for each non-zero bit index. + * @return {@code true} if all indexes return true from consumer, {@code false} otherwise. + * @throws NullPointerException if the specified action is null + */ + boolean processIndices(IntPredicate predicate); + + /** + * Creates an IndexExtractor comprising the unique indices for this extractor. + * + *

By default creates a new extractor with some overhead to remove + * duplicates. IndexExtractors that return unique indices by default + * should override this to return {@code this}.

+ * + *

The default implementation will filter the indices from this instance + * and return them in ascending order.

+ * + * @return the IndexExtractor of unique values. + * @throws IndexOutOfBoundsException if any index is less than zero. + */ + default IndexExtractor uniqueIndices() { + final BitSet bitSet = new BitSet(); + processIndices(i -> { + bitSet.set(i); + return true; + }); + + return new IndexExtractor() { + @Override + public boolean processIndices(final IntPredicate predicate) { + for (int idx = bitSet.nextSetBit(0); idx >= 0; idx = bitSet.nextSetBit(idx + 1)) { + if (!predicate.test(idx)) { + return false; + } + } + return true; + } + + @Override + public IndexExtractor uniqueIndices() { + return this; + } + }; + } +}