From 74c78ad798dddf8f2f6758978c7d69771efb9082 Mon Sep 17 00:00:00 2001 From: GerardPaligot Date: Tue, 1 Dec 2015 16:11:00 +0100 Subject: [PATCH] fix(noclasspath): Supports noclasspath for parameters. --- .../visitor/DefaultJavaPrettyPrinter.java | 9 +- .../support/compiler/jdt/JDTTreeBuilder.java | 8 +- .../spoon/test/parameters/ParameterTest.java | 29 ++ .../resources/parameter/FlowUtilities.java | 386 ++++++++++++++++++ 4 files changed, 425 insertions(+), 7 deletions(-) create mode 100644 src/test/java/spoon/test/parameters/ParameterTest.java create mode 100644 src/test/resources/parameter/FlowUtilities.java diff --git a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java index 897e2e177e6..0b839016b14 100644 --- a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java +++ b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java @@ -710,9 +710,12 @@ public void visitCtCase(CtCase caseStatement) { if (caseStatement.getCaseExpression() != null) { write("case "); // writing enum case expression - if ((caseStatement.getCaseExpression() instanceof CtFieldAccess)) { - CtFieldReference variable = ((CtFieldAccess) caseStatement.getCaseExpression()).getVariable(); - if (variable.getType().getQualifiedName().equals(variable.getDeclaringType().getQualifiedName())) { + if (caseStatement.getCaseExpression() instanceof CtFieldAccess) { + final CtFieldReference variable = ((CtFieldAccess) caseStatement.getCaseExpression()).getVariable(); + // In noclasspath mode, we don't have always the type of the declaring type. + if (variable.getType() != null + && variable.getDeclaringType() != null + && variable.getType().getQualifiedName().equals(variable.getDeclaringType().getQualifiedName())) { write(variable.getSimpleName()); } else { scan(caseStatement.getCaseExpression()); diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java index f4968d26d5a..4fe46cf3833 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -1788,16 +1788,16 @@ public boolean visit(Argument argument, BlockScope scope) { p.setSimpleName(new String(argument.name)); p.setVarArgs(argument.isVarArgs()); p.setModifiers(getModifiers(argument.modifiers)); - if (argument.type != null) { - p.setType(references.getTypeReference(argument.type.resolvedType)); - } else if (argument.binding != null && argument.binding.type != null) { - context.isLambdaParameterImplicitlyTyped = false; + if (argument.binding != null && argument.binding.type != null) { + context.isLambdaParameterImplicitlyTyped = argument.type != null; if (argument.binding.type instanceof WildcardBinding) { p.setType(references.getTypeReference((((WildcardBinding) argument.binding.type).bound))); } else { p.setType(references.getTypeReference((argument.binding.type))); } context.isLambdaParameterImplicitlyTyped = true; + } else if (argument.type != null) { + p.setType(references.getTypeReference(argument.type.resolvedType)); } context.enter(p, argument); if (argument.initialization != null) { diff --git a/src/test/java/spoon/test/parameters/ParameterTest.java b/src/test/java/spoon/test/parameters/ParameterTest.java new file mode 100644 index 00000000000..6d4b2e0b82b --- /dev/null +++ b/src/test/java/spoon/test/parameters/ParameterTest.java @@ -0,0 +1,29 @@ +package spoon.test.parameters; + +import org.junit.Test; +import spoon.Launcher; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.visitor.filter.NameFilter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ParameterTest { + @Test + public void testParameterInNoClasspath() throws Exception { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/resources/parameter"); + launcher.setSourceOutputDirectory("./target/parameter"); + launcher.getEnvironment().setNoClasspath(true); + launcher.run(); + + final CtClass aClass = launcher.getFactory().Class().get("org.eclipse.draw2d.text.FlowUtilities"); + final CtParameter parameter = aClass.getElements(new NameFilter>("font")).get(0); + + assertEquals("font", parameter.getSimpleName()); + assertNotNull(parameter.getType()); + assertEquals("org.eclipse.swt.graphics.Font", parameter.getType().toString()); + assertEquals("org.eclipse.swt.graphics.Font font", parameter.toString()); + } +} diff --git a/src/test/resources/parameter/FlowUtilities.java b/src/test/resources/parameter/FlowUtilities.java new file mode 100644 index 00000000000..f1356af454e --- /dev/null +++ b/src/test/resources/parameter/FlowUtilities.java @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.draw2d.text; + + +import com.ibm.icu.text.BreakIterator; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.graphics.TextLayout; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.draw2d.FigureUtilities; +import org.eclipse.draw2d.TextUtilities; + +/** + * Utility class for FlowFigures. + * @author hudsonr + * @since 3.4 + */ +public class FlowUtilities +{ + +interface LookAhead { + int getWidth(); +} + +/** + * a singleton default instance + */ +public static FlowUtilities INSTANCE = new FlowUtilities(); + +private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance(); +private static TextLayout layout; + +static final BreakIterator LINE_BREAK = BreakIterator.getLineInstance(); + +static boolean canBreakAfter(char c) { + boolean result = Character.isWhitespace(c) || c == '-'; + if (!result && (c < 'a' || c > 'z')) { + // chinese characters and such would be caught in here + // LINE_BREAK is used here because INTERNAL_LINE_BREAK might be in use + LINE_BREAK.setText(c + "a"); //$NON-NLS-1$ + result = LINE_BREAK.isBoundary(1); + } + return result; +} + +private static int findFirstDelimeter(String string) { + int macNL = string.indexOf('\r'); + int unixNL = string.indexOf('\n'); + + if (macNL == -1) + macNL = Integer.MAX_VALUE; + if (unixNL == -1) + unixNL = Integer.MAX_VALUE; + + return Math.min(macNL, unixNL); +} + +/** + * Gets the average character width. + * + * @param fragment the supplied TextFragmentBox to use for calculation. + * if the length is 0 or if the width is or below 0, + * the average character width is taken from standard + * font metrics. + * @param font the font to use in case the TextFragmentBox conditions + * above are true. + * @return the average character width + */ +protected float getAverageCharWidth(TextFragmentBox fragment, Font font) { + if (fragment.getWidth() > 0 && fragment.length != 0) + return fragment.getWidth() / (float)fragment.length; + return FigureUtilities.getFontMetrics(font).getAverageCharWidth(); +} + +static int getBorderAscent(InlineFlow owner) { + if (owner.getBorder() instanceof FlowBorder) { + FlowBorder border = (FlowBorder)owner.getBorder(); + return border.getInsets(owner).top; + } + return 0; +} + +static int getBorderAscentWithMargin(InlineFlow owner) { + if (owner.getBorder() instanceof FlowBorder) { + FlowBorder border = (FlowBorder)owner.getBorder(); + return border.getTopMargin() + border.getInsets(owner).top; + } + return 0; +} + +static int getBorderDescent(InlineFlow owner) { + if (owner.getBorder() instanceof FlowBorder) { + FlowBorder border = (FlowBorder)owner.getBorder(); + return border.getInsets(owner).bottom; + } + return 0; +} + +static int getBorderDescentWithMargin(InlineFlow owner) { + if (owner.getBorder() instanceof FlowBorder) { + FlowBorder border = (FlowBorder)owner.getBorder(); + return border.getBottomMargin() + border.getInsets(owner).bottom; + } + return 0; +} + +/** + * Provides a TextLayout that can be used by the Draw2d text package for Bidi. This + * TextLayout should not be disposed by clients. The provided TextLayout's orientation + * will be LTR. + * + * @return an SWT TextLayout that can be used for Bidi + * @since 3.1 + */ +static TextLayout getTextLayout() { + if (layout == null) + layout = new TextLayout(Display.getDefault()); + layout.setOrientation(SWT.LEFT_TO_RIGHT); + return layout; +} + +/** + * @param frag + * @param string + * @param font + * @since 3.1 + */ +private static void initBidi(TextFragmentBox frag, String string, Font font) { + if (frag.requiresBidi()) { + TextLayout textLayout = getTextLayout(); + textLayout.setFont(font); + //$TODO need to insert overrides in front of string. + textLayout.setText(string); + } +} + +private int measureString(TextFragmentBox frag, String string, int guess, Font font) { + if (frag.requiresBidi()) { + // The text and/or could have changed if the lookAhead was invoked. This will + // happen at most once. + return getTextLayoutBounds(string, font, 0, guess - 1).width; + } else + return getTextUtilities().getStringExtents(string.substring(0, guess), font).width; +} + +/** + * Sets up the fragment width based using the font and string passed in. + * + * @param fragment + * the text fragment whose width will be set + * @param font + * the font to be used in the calculation + * @param string + * the string to be used in the calculation + */ +final protected void setupFragment(TextFragmentBox fragment, Font font, String string) { + if (fragment.getWidth() == -1 || fragment.isTruncated()) { + int width; + if (string.length() == 0 || fragment.length == 0) + width = 0; + else if (fragment.requiresBidi()) { + width = getTextLayoutBounds(string, font, 0, fragment.length - 1).width; + } else + width = getTextUtilities().getStringExtents(string.substring(0, fragment.length), font).width; + if (fragment.isTruncated()) + width += getEllipsisWidth(font); + fragment.setWidth(width); + } +} + +/** + * Sets up a fragment and returns the number of characters consumed from the given + * String. An average character width can be provided as a hint for faster calculation. + * If a fragment's bidi level is set, a TextLayout will be used to calculate the width. + * + * @param frag the TextFragmentBox + * @param string the String + * @param font the Font used for measuring + * @param context the flow context + * @param wrapping the word wrap style + * @return the number of characters that will fit in the given space; can be 0 (eg., when + * the first character of the given string is a newline) + */ +final protected int wrapFragmentInContext(TextFragmentBox frag, String string, + FlowContext context, LookAhead lookahead, Font font, int wrapping) { + frag.setTruncated(false); + int strLen = string.length(); + if (strLen == 0) { + frag.setWidth(-1); + frag.length = 0; + setupFragment(frag, font, string); + context.addToCurrentLine(frag); + return 0; + } + + INTERNAL_LINE_BREAK.setText(string); + + initBidi(frag, string, font); + float avgCharWidth = getAverageCharWidth(frag, font); + frag.setWidth(-1); + + /* + * Setup initial boundaries within the string. + */ + int absoluteMin = 0; + int max, min = 1; + if (wrapping == ParagraphTextLayout.WORD_WRAP_HARD) { + absoluteMin = INTERNAL_LINE_BREAK.next(); + while (absoluteMin > 0 && Character.isWhitespace(string.charAt(absoluteMin - 1))) + absoluteMin--; + min = Math.max(absoluteMin, 1); + } + int firstDelimiter = findFirstDelimeter(string); + if (firstDelimiter == 0) + min = max = 0; + else + max = Math.min(strLen, firstDelimiter) + 1; + + + int availableWidth = context.getRemainingLineWidth(); + int guess = 0, guessSize = 0; + + while (true) { + if ((max - min) <= 1) { + if (min == absoluteMin + && context.isCurrentLineOccupied() + && !context.getContinueOnSameLine() + && availableWidth < measureString(frag, string, min, font) + + ((min == strLen && lookahead != null) ? lookahead.getWidth() : 0) + ) { + context.endLine(); + availableWidth = context.getRemainingLineWidth(); + max = Math.min(strLen, firstDelimiter) + 1; + if ((max - min) <= 1) + break; + } else + break; + } + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess += 0.5f + (availableWidth - guessSize) / avgCharWidth; + + if (guess >= max) guess = max - 1; + if (guess <= min) guess = min + 1; + + guessSize = measureString(frag, string, guess, font); + + if (guess == strLen + && lookahead != null + && !canBreakAfter(string.charAt(strLen - 1)) + && guessSize + lookahead.getWidth() > availableWidth) { + max = guess; + continue; + } + + if (guessSize <= availableWidth) { + min = guess; + frag.setWidth(guessSize); + if (guessSize == availableWidth) + max = guess + 1; + } else + max = guess; + } + + int result = min; + boolean continueOnLine = false; + if (min == strLen) { + //Everything fits + if (string.charAt(strLen - 1) == ' ') { + if (frag.getWidth() == -1) { + frag.length = result; + frag.setWidth(measureString(frag, string, result, font)); + } + if (lookahead.getWidth() > availableWidth - frag.getWidth()) { + frag.length = result - 1; + frag.setWidth(-1); + } else + frag.length = result; + } else { + continueOnLine = !canBreakAfter(string.charAt(strLen - 1)); + frag.length = result; + } + } else if (min == firstDelimiter) { + //move result past the delimiter + frag.length = result; + if (string.charAt(min) == '\r') { + result++; + if (++min < strLen && string.charAt(min) == '\n') + result++; + } else if (string.charAt(min) == '\n') + result++; + } else if (string.charAt(min) == ' ' + || canBreakAfter(string.charAt(min - 1)) + || INTERNAL_LINE_BREAK.isBoundary(min)) { + frag.length = min; + if (string.charAt(min) == ' ') + result++; + else if (string.charAt(min - 1) == ' ') { + frag.length--; + frag.setWidth(-1); + } + } else out: { + // In the middle of an unbreakable offset + result = INTERNAL_LINE_BREAK.preceding(min); + if (result == 0) { + switch (wrapping) { + case ParagraphTextLayout.WORD_WRAP_TRUNCATE : + int truncatedWidth = availableWidth - getEllipsisWidth(font); + if (truncatedWidth > 0) { + //$TODO this is very slow. It should be using avgCharWidth to go faster + while (min > 0) { + guessSize = measureString(frag, string, min, font); + if (guessSize <= truncatedWidth) + break; + min--; + } + frag.length = min; + } else + frag.length = 0; + frag.setTruncated(true); + result = INTERNAL_LINE_BREAK.following(max - 1); + break out; + + default: + result = min; + break; + } + } + frag.length = result; + if (string.charAt(result - 1) == ' ') + frag.length--; + frag.setWidth(-1); + } + + setupFragment(frag, font, string); + context.addToCurrentLine(frag); + context.setContinueOnSameLine(continueOnLine); + return result; +} + +/** + * @see TextLayout#getBounds() + */ +protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) { + TextLayout textLayout = getTextLayout(); + textLayout.setFont(f); + textLayout.setText(s); + return textLayout.getBounds(start, end); +} + +/** + * Returns an instance of a TextUtililities class on which + * text calculations can be performed. Clients may override to customize. + * + * @return the TextUtililities instance + * @since 3.4 + */ +protected TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +/** + * Gets the ellipsis width. + * + * @param font + * the font to be used in the calculation + * @return the width of the ellipsis + * @since 3.4 + */ +private int getEllipsisWidth(Font font) { + return getTextUtilities().getStringExtents(TextFlow.ELLIPSIS, font).width; +} +}