diff --git a/.github/workflows/full-check.yml b/.github/workflows/full-check.yml index 3c0d1f2a998..9d98ad5f809 100644 --- a/.github/workflows/full-check.yml +++ b/.github/workflows/full-check.yml @@ -1,7 +1,7 @@ -# Run all tests and builds all aspects of GWT using Java 8. Runs nightly (plus -# or minus timzeones) on the main branch, and will also run right away on a -# push to a release branch. Release zips are uploaded as part of the build, -# though maven snapshots are not yet deployed. +# Run all tests and builds all aspects of GWT using Java 8, 11, and 17. Runs +# nightly (plus or minus timzeones) on the main branch, and will also run right +# away on a push to a release branch. Release zips are uploaded as part of the +# build, though maven snapshots are not yet deployed. name: Full build on: schedule: @@ -18,6 +18,9 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + java-version: [ '8', '11', '17' ] steps: - name: Checkout GWT itself into one directory uses: actions/checkout@v2 @@ -28,11 +31,11 @@ jobs: with: repository: 'gwtproject/tools' path: 'tools' - - name: Set up JDK 8 - # GWT presently requires Java8 to build, but can run on newer Java versions + - name: Set up JDK ${{ matrix.java-version }} + # GWT presently requires Java8 to build just the SDK and some tests, or 11 to build everything, but can run on newer Java versions uses: actions/setup-java@v3 with: - java-version: '8' + java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Build, style/api checks, test, produce docs @@ -62,27 +65,27 @@ jobs: uses: actions/upload-artifact@v2 if: always() with: - name: checkstyle-reports + name: checkstyle-reports-java${{ matrix.java-version }} path: 'gwt/build/out/**/checkstyle*.xml' - name: Upload test xml files for manual review in its own artifact uses: actions/upload-artifact@v2 if: always() with: - name: junit-reports + name: junit-reports-java${{ matrix.java-version }} path: 'gwt/build/out/**/test/**/reports/TEST-*.xml' - name: On success, upload the release zip uses: actions/upload-artifact@v2 with: - name: gwt + name: gwt-java${{ matrix.java-version }} path: 'gwt/build/dist/gwt-*.zip' - name: Set up sonatype credentials # Using the same java version as above, set up a settings.xml file uses: actions/setup-java@v3 - if: github.event_name == 'schedule' && github.repository_owner == 'gwtproject' + if: ${{ github.event_name == 'schedule' && github.repository_owner == 'gwtproject' && matrix.java-version == '17' }} with: - java-version: '8' + java-version: ${{ matrix.java-version }} distribution: 'temurin' # Define the ID for the server to put in settings.xml, and the env vars that will contain the secrets server-id: sonatype-snapshots @@ -90,7 +93,7 @@ jobs: server-password: SONATYPE_PASSWORD - name: Nightly builds should be deployed as snapshots to sonatype - if: github.event_name == 'schedule' && github.repository_owner == 'gwtproject' + if: ${{ github.event_name == 'schedule' && github.repository_owner == 'gwtproject' && matrix.java-version == '17' }} env: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/.github/workflows/quick-check.yml b/.github/workflows/quick-check.yml index c4755ad99ad..75c861c4699 100644 --- a/.github/workflows/quick-check.yml +++ b/.github/workflows/quick-check.yml @@ -6,6 +6,9 @@ on: [push, pull_request] jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + java-version: ['8', '11', '17'] steps: - name: Checkout GWT itself into one directory uses: actions/checkout@v2 @@ -18,11 +21,11 @@ jobs: with: repository: 'gwtproject/tools' path: 'tools' - - name: Set up JDK 8 - # GWT presently requires Java8 to build, but can run on newer Java versions - uses: actions/setup-java@v2 + - name: Set up JDK ${{ matrix.java-version }} + # GWT presently requires Java8 to build just the SDK and some tests, or 11+ to build everything, and can run on newer Java versions + uses: actions/setup-java@v3 with: - java-version: '8' + java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Set up reviewdog for easier checks on the PR's checkstyle output uses: reviewdog/action-setup@v1 @@ -41,8 +44,8 @@ jobs: ANT_OPTS=-Xmx2g ant clean dist doc checkstyle apicheck - - name: Create pull request comments/annotations for checkstyle, even on failure - if: always() && github.event_name == 'pull_request' + - name: Create pull request comments/annotations for checkstyle from the java 17 build, even on failure + if: ${{ always() && github.event_name == 'pull_request' && matrix.java-version == '17' }} env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -55,8 +58,8 @@ jobs: done - name: Upload checkstyle xml for manual review uses: actions/upload-artifact@v2 - if: always() + if: ${{ matrix.java-version == '17' }} with: - name: checkstyle-reports + name: checkstyle-reports-java${{ matrix.java-version }} path: 'gwt/build/out/**/checkstyle*.xml' retention-days: 5 diff --git a/README.md b/README.md index a91c49ce934..71354ce75ba 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,6 @@ `$ ant clean dist-dev` - or if you don't have `python` and `g++` just run - - `$ ant clean dist-dev` - Then you will get all `.jar` files in the folder `build/lib` and the redistributable file will be: `build/dist/gwt-0.0.0.zip` @@ -42,6 +38,8 @@ - To compile everything including examples you have to run `$ ant clean dist` + + - To create maven artifacts (after building .jar using ant), use [following guide](./maven/README.txt). ### How to verify GWT code conventions: diff --git a/build.xml b/build.xml index c10eea6f2c5..4ab2c6e8f58 100755 --- a/build.xml +++ b/build.xml @@ -98,7 +98,7 @@ - + diff --git a/build_tools/doctool/build.xml b/build_tools/doctool/build.xml index abd3026bae9..c4870197b61 100644 --- a/build_tools/doctool/build.xml +++ b/build_tools/doctool/build.xml @@ -3,17 +3,15 @@ - - - - - - - + + - + diff --git a/build_tools/doctool/src/com/google/doctool/JreDocTool.java b/build_tools/doctool/src/com/google/doctool/JreDocTool.java deleted file mode 100644 index 55195bf6640..00000000000 --- a/build_tools/doctool/src/com/google/doctool/JreDocTool.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.doctool; - -import com.google.doctool.custom.EztDoclet; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Uses the Javadoc tool to produce wiki output documenting the JRE emulation - * classes in GWT. - */ -public class JreDocTool { - - public static void main(String[] args) { - JreDocToolFactory factory = new JreDocToolFactory(); - String arg; - - for (int i = 0; i < args.length; i++) { - if (tryParseFlag(args, i, "-help")) { - printHelp(); - return; - } else if (null != (arg = tryParseArg(args, i, "-classpath"))) { - i++; - factory.setClasspath(arg); - } else if (null != (arg = tryParseArg(args, i, "-out"))) { - i++; - factory.setOutputFile(arg); - } else if (null != (arg = tryParseArg(args, i, "-packages"))) { - i++; - factory.setPackages(arg); - } else if (null != (arg = tryParseArg(args, i, "-sourcepath"))) { - i++; - factory.setSourcepath(arg); - } - } - - JreDocTool docTool = factory.create(System.err); - if (docTool != null) { - docTool.process(); - } - } - - /** - * Prints help for using JreDocTool. - */ - private static void printHelp() { - String s = ""; - s += "JreDocTool\n"; - s += " Creates EZT format member listing from Java source"; - s += " for emulated JRE classes.\n"; - s += "\n"; - s += "Required arguments:\n"; - s += " -classpath\n"; - s += " The path to find imported classes for this doc set.\n"; - s += " -sourcepath\n"; - s += " The path to find Java source for this doc set.\n"; - s += " E.g. /gwt/src/trunk/user/super/com/google/gwt/emul\n"; - s += " -out\n"; - s += " The path and filename of the output file\n"; - s += " -packages\n"; - s += " A semicolon-separated list of fully-qualified package names.\n"; - s += " E.g. java.lang;java.lang.annotation;java.util;java.io;java.sql\n"; - s += "\n"; - System.out.println(s); - } - - /** - * Parse a flag with a argument. - */ - private static String tryParseArg(String[] args, int i, String name) { - if (i < args.length) { - if (args[i].equals(name)) { - if (i + 1 < args.length) { - String arg = args[i + 1]; - if (arg.startsWith("-")) { - System.out.println("Warning: arg to " + name - + " looks more like a flag: " + arg); - } - return arg; - } else { - throw new IllegalArgumentException("Expecting an argument after " - + name); - } - } - } - return null; - } - - /** - * Parse just a flag with no subsequent argument. - */ - private static boolean tryParseFlag(String[] args, int i, String name) { - if (i < args.length) { - if (args[i].equals(name)) { - return true; - } - } - return false; - } - - private String classpath; - - private String outputFile; - - private String packages; - - private String sourcepath; - - JreDocTool(String classpath, String outputFile, String packages, - String sourcepath) { - this.classpath = classpath; - this.outputFile = outputFile; - this.packages = packages; - this.sourcepath = sourcepath; - } - - private void process() { - List args = new ArrayList(); - - args.add("-public"); - - args.add("-quiet"); - - args.add("-source"); - args.add("1.8"); - - args.add("-doclet"); - args.add(EztDoclet.class.getName()); - - args.add("-classpath"); - args.add(this.classpath); - - args.add("-sourcepath"); - args.add(this.sourcepath); - - args.add(EztDoclet.OPT_EZTFILE); - args.add(this.outputFile); - - args.addAll(Arrays.asList(this.packages.split(";"))); - - com.sun.tools.javadoc.Main.execute(args.toArray(new String[0])); - } -} diff --git a/build_tools/doctool/src/com/google/doctool/JreDocToolFactory.java b/build_tools/doctool/src/com/google/doctool/JreDocToolFactory.java deleted file mode 100644 index 0e7ac661f75..00000000000 --- a/build_tools/doctool/src/com/google/doctool/JreDocToolFactory.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.doctool; - -import java.io.PrintStream; - -/** - * Supports two-phase creation of {@link JreDocTool} objects. - */ -public class JreDocToolFactory { - - private String classpath; - - private String outputFile; - - private String packages; - - private String sourcepath; - - public JreDocTool create(PrintStream err) { - if (this.classpath == null) { - err.println("You must specify the -classpath"); - return null; - } - - if (this.outputFile == null) { - err.println("You must specify the output file (-out)"); - return null; - } - - if (this.packages == null) { - err.println("You must specify the -packages"); - return null; - } - - if (this.sourcepath == null) { - err.println("You must specify the -sourcepath"); - return null; - } - - return new JreDocTool(classpath, outputFile, packages, sourcepath); - } - - public void setClasspath(String classpath) { - this.classpath = classpath; - } - - public void setOutputFile(String outputFile) { - this.outputFile = outputFile; - } - - public void setPackages(String packages) { - this.packages = packages; - } - - public void setSourcepath(String sourcePath) { - this.sourcepath = sourcePath; - } -} diff --git a/build_tools/doctool/src/com/google/doctool/LinkResolver.java b/build_tools/doctool/src/com/google/doctool/LinkResolver.java deleted file mode 100644 index f16777d5470..00000000000 --- a/build_tools/doctool/src/com/google/doctool/LinkResolver.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2006 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.doctool; - -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.Doc; -import com.sun.javadoc.MemberDoc; -import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.Parameter; -import com.sun.javadoc.SourcePosition; -import com.sun.javadoc.Tag; - -/** - * Methods related to resolving cross-references in source. - */ -public class LinkResolver { - - /** - * Abstracts out mechanisms for finding different bits of doc. - */ - public interface ExtraClassResolver { - ClassDoc findClass(String className); - } - - public static SourcePosition resolveLink(Tag tag) { - return resolveLink(tag, null); - } - - public static SourcePosition resolveLink(Tag tag, - ExtraClassResolver classResolver) { - String linkText = tag.text(); - String className; - String methodSig; - int pos = linkText.indexOf('#'); - if (pos >= 0) { - className = linkText.substring(0, pos); - methodSig = linkText.substring(pos + 1); - } else { - className = linkText; - methodSig = null; - } - - ClassDoc containingClass = null; - Doc holder = tag.holder(); - if (holder instanceof ClassDoc) { - containingClass = (ClassDoc) holder; - } else if (holder instanceof MemberDoc) { - containingClass = ((MemberDoc) holder).containingClass(); - } - - ClassDoc targetClass = null; - if (className.length() == 0) { - targetClass = containingClass; - } else if (holder instanceof PackageDoc) { - targetClass = ((PackageDoc) holder).findClass(className); - } else { - targetClass = containingClass.findClass(className); - } - - if (targetClass == null) { - if (classResolver != null) { - targetClass = classResolver.findClass(className); - } - if (targetClass == null) { - System.err.println(tag.position().toString() - + ": unable to resolve class " + className + " for " + tag); - System.exit(1); - } - } - - if (methodSig == null) { - return targetClass.position(); - } - - String paramSig = methodSig.substring(methodSig.indexOf('(') + 1, - methodSig.lastIndexOf(')')); - String[] resolvedParamTypes; - if (paramSig.length() > 0) { - String[] unresolvedParamTypes = paramSig.split("\\s*,\\s*"); - resolvedParamTypes = new String[unresolvedParamTypes.length]; - for (int i = 0; i < unresolvedParamTypes.length; ++i) { - ClassDoc paramType = containingClass.findClass(unresolvedParamTypes[i]); - if (paramType == null && classResolver != null) { - paramType = classResolver.findClass(unresolvedParamTypes[i]); - } - if (paramType == null) { - System.err.println(tag.position().toString() - + ": unable to resolve class " + unresolvedParamTypes[i] - + " for " + tag); - System.exit(1); - } - resolvedParamTypes[i] = paramType.qualifiedTypeName(); - } - } else { - resolvedParamTypes = new String[0]; - } - - String possibleOverloads = ""; - - MethodDoc[] methods = targetClass.methods(); - outer : for (int i = 0; i < methods.length; ++i) { - MethodDoc methodDoc = methods[i]; - if (methodSig.startsWith(methodDoc.name())) { - possibleOverloads += "\n" + methodDoc.flatSignature(); - Parameter[] tryParams = methodDoc.parameters(); - if (resolvedParamTypes.length != tryParams.length) { - // param count mismatch - continue outer; - } - for (int j = 0; j < tryParams.length; ++j) { - if (!tryParams[j].type().qualifiedTypeName().equals( - resolvedParamTypes[j])) { - // param type mismatch - continue outer; - } - } - return methodDoc.position(); - } - } - - System.err.println(tag.position().toString() - + ": unable to resolve method for " + tag); - if (possibleOverloads.length() > 0) { - System.err.println("Possible overloads: " + possibleOverloads); - } - System.exit(1); - return null; - } - -} diff --git a/build_tools/doctool/src/com/google/doctool/ResourceIncluder.java b/build_tools/doctool/src/com/google/doctool/ResourceIncluder.java index 97833c298f6..71fe1a01d22 100644 --- a/build_tools/doctool/src/com/google/doctool/ResourceIncluder.java +++ b/build_tools/doctool/src/com/google/doctool/ResourceIncluder.java @@ -15,12 +15,11 @@ */ package com.google.doctool; -import com.sun.javadoc.Tag; - import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; /** * Utility methods related to including external resources in doc. @@ -28,30 +27,23 @@ public class ResourceIncluder { /** - * Copied from {@link com.google.gwt.util.tools.Utility#close(InputStream)}. + * Copied from {@link com.google.gwt.util.tools.Utility#close(AutoCloseable)}. */ - public static void close(InputStream is) { + public static void close(AutoCloseable is) { try { if (is != null) { is.close(); } - } catch (IOException e) { + } catch (Exception e) { } } - public static String getResourceFromClasspathScrubbedForHTML(Tag tag) { - String partialPath = tag.text(); - try { - String contents; - contents = getFileFromClassPath(partialPath); - contents = scrubForHtml(contents); - return contents; - } catch (IOException e) { - System.err.println(tag.position().toString() - + ": unable to include resource " + partialPath + " for tag " + tag); - System.exit(1); - return null; - } + public static String getResourceFromClasspathScrubbedForHTML(String partialPath) + throws IOException { + String contents; + contents = getFileFromClassPath(partialPath); + contents = scrubForHtml(contents); + return contents; } /** @@ -67,11 +59,20 @@ private static String getFileFromClassPath(String partialPath) throw new FileNotFoundException(partialPath); } ByteArrayOutputStream os = new ByteArrayOutputStream(); - int ch; - while ((ch = in.read()) != -1) { - os.write(ch); + byte[] buffer = new byte[1024]; + int bytesRead; + while (true) { + bytesRead = in.read(buffer); + if (bytesRead >= 0) { + // Copy the bytes out. + os.write(buffer, 0, bytesRead); + } else { + // End of input stream. + break; + } } - return new String(os.toByteArray(), "UTF-8"); + + return os.toString(StandardCharsets.UTF_8); } finally { close(in); } diff --git a/build_tools/doctool/src/com/google/doctool/custom/AbstractTaglet.java b/build_tools/doctool/src/com/google/doctool/custom/AbstractTaglet.java new file mode 100644 index 00000000000..9c2959757a7 --- /dev/null +++ b/build_tools/doctool/src/com/google/doctool/custom/AbstractTaglet.java @@ -0,0 +1,110 @@ +/* + * Copyright 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.doctool.custom; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.EndElementTree; +import com.sun.source.doctree.EntityTree; +import com.sun.source.doctree.StartElementTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.util.DocTreeScanner; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Taglet; + +import javax.lang.model.element.Element; +import javax.tools.Diagnostic; + +/** + * Abstract taglet baseclass to facilitate some basic operations. + */ +public abstract class AbstractTaglet implements Taglet { + protected DocletEnvironment env; + + @Override + public void init(DocletEnvironment env, Doclet doclet) { + this.env = env; + } + + /** + * Given a tag, returns the html within that tag. + * + * @param tag the tag to read the text from + * @return the tags, entities, and text within that tag, as an html string + */ + protected String getHtmlContent(DocTree tag) { + StringBuilder sb = new StringBuilder(); + tag.accept(new DocTreeScanner() { + @Override + public Void visitStartElement(StartElementTree node, Void unused) { + sb.append("<").append(node.getName()); + if (!node.getAttributes().isEmpty()) { + sb.append(" "); + // super only scans attributes + super.visitStartElement(node, unused); + } + if (node.isSelfClosing()) { + sb.append("/>"); + } else { + sb.append(">"); + } + + return null; + } + + @Override + public Void visitEndElement(EndElementTree node, Void unused) { + sb.append(""); + return null; + } + + @Override + public Void visitText(TextTree node, Void unused) { + // Only includes non-entity text + sb.append(node.getBody()); + return null; + } + + @Override + public Void visitEntity(EntityTree node, Void unused) { + // Entities need to be wrapped in &/;, but we aren't worrying about illegal + // entities here, assuming they are handled elsewhere + sb.append("&").append(node.getName()).append(";"); + return null; + } + }, null); + return sb.toString(); + } + + /** + * Helper method to log a diagnostic message to the user about a particular element+doctree. + * + * @param kind the kind of message to log + * @param message the message text to write + * @param element the element that the message applies to + * @param docTree the doctree node that the message applies to + */ + protected void printMessage(Diagnostic.Kind kind, String message, Element element, + DocTree docTree) { + env.getDocTrees().printMessage( + kind, + message, + docTree, + env.getDocTrees().getDocCommentTree(element), + env.getDocTrees().getPath(element).getCompilationUnit() + ); + } +} diff --git a/build_tools/doctool/src/com/google/doctool/custom/ExampleTaglet.java b/build_tools/doctool/src/com/google/doctool/custom/ExampleTaglet.java index 788e604f67d..544a6a4535b 100644 --- a/build_tools/doctool/src/com/google/doctool/custom/ExampleTaglet.java +++ b/build_tools/doctool/src/com/google/doctool/custom/ExampleTaglet.java @@ -15,116 +15,154 @@ */ package com.google.doctool.custom; -import com.google.doctool.LinkResolver; -import com.google.doctool.LinkResolver.ExtraClassResolver; +import com.sun.source.doctree.DocTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.DocTreePath; +import com.sun.source.util.DocTrees; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.SourcePosition; -import com.sun.javadoc.Tag; -import com.sun.tools.doclets.Taglet; - -import java.io.BufferedReader; -import java.io.FileReader; import java.io.IOException; -import java.util.Map; +import javax.lang.model.element.Element; +import javax.tools.Diagnostic; +import java.io.Reader; +import java.nio.CharBuffer; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; /** * A taglet for slurping examples into javadoc output. */ -public class ExampleTaglet implements Taglet { - - public static void register(Map tagletMap) { - ExampleTaglet tag = new ExampleTaglet(); - Taglet t = (Taglet) tagletMap.get(tag.getName()); - if (t != null) { - tagletMap.remove(tag.getName()); - } - tagletMap.put(tag.getName(), tag); - } +public class ExampleTaglet extends AbstractTaglet { + @Override public String getName() { return "example"; } - public boolean inConstructor() { - return true; - } + @Override + public String toString(List list, Element element) { + StringBuilder results = new StringBuilder(); + DocTrees trees = env.getDocTrees(); + for (DocTree tag : list) { + String linkText = getHtmlContent(tag); + + // Using the linktext and the current element as context, find the referenced Element if any + final Element targetElement = trees.getElement( + new DocTreePath( + new DocTreePath( + trees.getPath(element), + trees.getDocCommentTree(element) + ), + trees.getDocTreeFactory().newReferenceTree(linkText) + ) + ); + if (targetElement == null) { + String message = "Unable to resolve " + linkText + " found in javadoc for " + element; + printMessage(Diagnostic.Kind.ERROR, message, element, tag); + // return empty so the docs continue, fail + return ""; + } - public boolean inField() { - return true; - } + // having found the specified element, get its position in the source file + Tree tree = trees.getTree(targetElement); - public boolean inMethod() { - return true; - } + // find the compilation unit that was contains this + CompilationUnitTree cu = trees.getPath(targetElement).getCompilationUnit(); + try (Reader reader = cu.getSourceFile().openReader(false)) { + long startPosition = trees.getSourcePositions().getStartPosition(cu, tree); - public boolean inOverview() { - return true; - } + String slurpSource = slurpSource(reader, startPosition); + // The
 tag still requires '<' and '>' characters to be escaped
+        slurpSource = slurpSource.replace("<", "<");
+        slurpSource = slurpSource.replace(">", ">");
+        results.append("
").append(slurpSource).append("
"); + } catch (IOException e) { + e.printStackTrace(); + } + } - public boolean inPackage() { - return true; + return results.toString(); } - public boolean inType() { - return true; + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(Location.class); } + @Override public boolean isInlineTag() { return true; } - public String toString(Tag tag) { - SourcePosition position = LinkResolver.resolveLink(tag, - new ExtraClassResolver() { - public ClassDoc findClass(String className) { - return GWTJavaDoclet.root.classNamed(className); + /** + * Read the content of the reader line by line until the start character is found in a line. + * Remove leading whitespace on each line to match the first, and stop when closing curly + * brackets match opening brackets. + * + * @param reader the reader to get bytes from + * @param startChar the first character of the file that we're interested in + * @return a newline-joined, html-escaped string + */ + private static String slurpSource(Reader reader, long startChar) throws IOException { + // We want to skip until the line that contains character startChar - can't use BufferedReader + // for this as it won't count characters (and the newlines could be two chars etc) + + // style guide says 100, actual max is around 190 + CharBuffer line = CharBuffer.allocate(250); + // position starts at 0, since we incr at the same time as we read + int position = -1; + // accumulate finished lines to return + StringBuilder lines = new StringBuilder(); + // indent starts at -1 meaning "we don't know the indentation level yet" + int indent = -1; + + // braceDepth lets us tell when we finish the target element, but we have to be sure we saw at + // least one open/close set, or semicolon. + int braceDepth = 0; + boolean seenSemiColonOrBrace = false; + + // read each character - if \r (check for a \n to follow) or \n end the line and record the + // position of the end of the line. + int ch = reader.read(); + position++; + while (true) { + if (ch == '\n' || ch == '\r' || ch == -1) { + if (ch == '\r') { + ch = reader.read(); + position++; + if (ch == '\n') { + ch = reader.read(); + position++; } - }); - - String slurpSource = slurpSource(position); - // The
 tag still requires '<' and '>' characters to be escaped
-    slurpSource = slurpSource.replace("<", "<");
-    slurpSource = slurpSource.replace(">", ">");
-    return "
" + slurpSource + "
"; - } - - public String toString(Tag[] tags) { - if (tags == null || tags.length == 0) { - return null; - } - String result = ""; - for (int i = 0; i < tags.length; i++) { - result += toString(tags[i]); - } - return result; - } - - private static String slurpSource(SourcePosition position) { - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(position.file())); - for (int i = 0, n = position.line() - 1; i < n; ++i) { - br.readLine(); - } - - StringBuffer lines = new StringBuffer(); - String line = br.readLine(); - int braceDepth = 0; - int indent = -1; - boolean seenSemiColonOrBrace = false; - while (line != null) { + } else if (ch == '\n') { + ch = reader.read(); + position++; + } // else EOF, nothing to do + + // We've read a full line (and are ready to start the next one). + // If position < startChar, we haven't yet reached our content, continue + if (position <= startChar) { + line.clear(); + continue; + } + // make the written chars available to be read + line.flip(); if (indent == -1) { + // this is our first line, read the indentation level for (indent = 0; Character.isWhitespace(line.charAt(indent)); ++indent) { // just accumulate } } - + // remove the indent chars from this line, and append it + String lineStr; if (line.length() >= indent) { - line = line.substring(indent); + lineStr = line.subSequence(indent, line.length()).toString(); + } else { + lineStr = line.toString(); } - lines.append(line).append("\n"); + lines.append(lineStr).append('\n'); for (int i = 0, n = line.length(); i < n; ++i) { char c = line.charAt(i); if (c == '{') { @@ -137,26 +175,23 @@ private static String slurpSource(SourcePosition position) { } } - if (braceDepth > 0 || !seenSemiColonOrBrace) { - line = br.readLine(); - } else { + // now that we've written the line, check if it was our final line + if ((braceDepth <= 0 && seenSemiColonOrBrace) || ch == -1) { break; } - } - return lines.toString(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - if (br != null) { - br.close(); - } - } catch (IOException e) { - e.printStackTrace(); + // free up the buffer to have new chars written to it + line.clear(); + } else { + // we're in the middle of a line, record the character and continue + line.put((char) ch); + + ch = reader.read(); + position++; } } - return ""; + + return lines.toString(); } } diff --git a/build_tools/doctool/src/com/google/doctool/custom/EztDoclet.java b/build_tools/doctool/src/com/google/doctool/custom/EztDoclet.java deleted file mode 100644 index 37853a92338..00000000000 --- a/build_tools/doctool/src/com/google/doctool/custom/EztDoclet.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.doctool.custom; - -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.DocErrorReporter; -import com.sun.javadoc.ExecutableMemberDoc; -import com.sun.javadoc.FieldDoc; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.RootDoc; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; - -/** - * A doclet for using producing EZT output listing the specified classes and - * their methods and constructors. - */ -public class EztDoclet { - - public static final String OPT_EZTFILE = "-eztfile"; - - private static EztDoclet EZT_DOCLET; - - private static final String JAVADOC_URL = "https://docs.oracle.com/javase/8/docs/api/"; - - public static int optionLength(String option) { - if (option.equals(OPT_EZTFILE)) { - return 2; - } - return 0; - } - - public static boolean start(RootDoc root) { - getDoclet().process(root); - return true; - } - - public static boolean validOptions(String[][] options, - DocErrorReporter reporter) { - return getDoclet().analyzeOptions(options, reporter); - } - - private static EztDoclet getDoclet() { - if (EZT_DOCLET == null) { - EZT_DOCLET = new EztDoclet(); - } - return EZT_DOCLET; - } - - private String outputFile; - - private boolean analyzeOptions(String[][] options, DocErrorReporter reporter) { - for (int i = 0; i < options.length; i++) { - if (options[i][0] == OPT_EZTFILE) { - outputFile = options[i][1]; - } - } - - if (outputFile == null) { - reporter.printError("You must specify an output filepath with " - + OPT_EZTFILE); - return false; - } - - return true; - } - - private String createFieldList(Collection fields) { - StringBuffer buffer = new StringBuffer(); - Iterator iter = fields.iterator(); - while (iter.hasNext()) { - FieldDoc field = iter.next(); - buffer.append(field.name()); - if (iter.hasNext()) { - buffer.append(", "); - } - } - return buffer.toString(); - } - - private String createMemberList(Collection members) { - StringBuffer buffer = new StringBuffer(); - Iterator iter = members.iterator(); - while (iter.hasNext()) { - ExecutableMemberDoc member = iter.next(); - buffer.append(member.name() + member.flatSignature()); - if (iter.hasNext()) { - buffer.append(", "); - } - } - return buffer.toString(); - } - - private void process(RootDoc root) { - try { - File outFile = new File(outputFile); - outFile.getParentFile().mkdirs(); - FileWriter fw = new FileWriter(outFile); - PrintWriter pw = new PrintWriter(fw, true); - - pw.println("
    "); - for (PackageDoc pack : root.specifiedPackages()) { - pw.format("
  1. %s
  2. \n", - pack.name().replace('.', '_'), pack.name()); - } - pw.println("
\n"); - - for (PackageDoc pack : root.specifiedPackages()) { - pw.format("

Package %s

\n", - pack.name().replace('.', '_'), pack.name()); - pw.println("
"); - - String packURL = JAVADOC_URL + pack.name().replace(".", "/") + "/"; - - // Sort the classes alphabetically - ClassDoc[] classes = pack.allClasses(true); - Arrays.sort(classes, new Comparator() { - public int compare(ClassDoc arg0, ClassDoc arg1) { - return arg0.name().compareTo(arg1.name()); - } - }); - - Iterator iter = Arrays.asList(classes).iterator(); - while (iter.hasNext()) { - ClassDoc cls = iter.next(); - - // Each class links to Sun's main JavaDoc - pw.format("
%s
\n", packURL, - cls.name(), cls.name()); - - // Print out all fields - Collection fields = new ArrayList(); - fields.addAll(Arrays.asList(cls.fields(true))); - - if (!fields.isEmpty()) { - pw.format("
%s
\n", createFieldList(fields)); - } - - // Print out all constructors and methods - Collection members = new ArrayList(); - members.addAll(Arrays.asList(cls.constructors(true))); - members.addAll(Arrays.asList(cls.methods(true))); - - if (!members.isEmpty()) { - pw.format("
%s
\n", createMemberList(members)); - } - - if (iter.hasNext()) { - pw.print("\n"); - } - } - - pw.println("
\n"); - } - pw.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/build_tools/doctool/src/com/google/doctool/custom/FindPackages.java b/build_tools/doctool/src/com/google/doctool/custom/FindPackages.java index ee280bd1b98..fb00872b4a5 100644 --- a/build_tools/doctool/src/com/google/doctool/custom/FindPackages.java +++ b/build_tools/doctool/src/com/google/doctool/custom/FindPackages.java @@ -26,7 +26,7 @@ import java.util.regex.Pattern; /** - * Used by trunk/doc/build.xml to generate the packages.properties file. + * Used by doc/build.xml to generate the packages.properties file. */ public class FindPackages { @@ -55,13 +55,20 @@ public class FindPackages { private static final String[] JAVA_PKGS = { "java.beans", "java.io", - "java.lang", "java.lang.annotation", "java.lang.reflect", + "java.lang", + "java.lang.annotation", + "java.lang.reflect", "java.math", "java.nio.charset", "java.security", "java.sql", "java.text", - "java.util", "java.util.concurrent", "java.util.concurrent.atomic", "java.util.function", "java.util.logging", "java.util.stream" + "java.util", + "java.util.concurrent", + "java.util.concurrent.atomic", + "java.util.function", + "java.util.logging", + "java.util.stream" }; /** @@ -90,14 +97,14 @@ public class FindPackages { "user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java", "user/src/com/google/gwt/i18n/server/GwtLocaleImpl.java"}; - private static Pattern exclusions; + private static final Pattern exclusions; static { StringBuilder sb = new StringBuilder(); for (int i = 0; i < EXCLUSIONS.length; i++) { String ex = EXCLUSIONS[i]; ex = ex.replace(".", "\\."); if (i < EXCLUSIONS.length - 1) { - sb.append(ex + "|"); + sb.append(ex).append("|"); } else { sb.append(ex); } @@ -128,14 +135,16 @@ public static void main(String[] args) { out.println("JAVA_PKGS=\\"); for (int i = 0; i < JAVA_PKGS.length; i++) { if (i < JAVA_PKGS.length - 1) { - out.println(JAVA_PKGS[i] + ";\\"); + out.println(JAVA_PKGS[i] + ",\\"); } else { out.println(JAVA_PKGS[i]); } } out.println("# The last package should not have a trailing semicolon"); - out.println(""); - out.println("# Individual classes to include when we don't want to include an entire package"); + out.println(); + out.println( + "# Individual classes to include when we don't want to include an entire package" + ); out.println("USER_CLASSES=\\"); // Output a package-info.java once for each package @@ -145,15 +154,15 @@ public static void main(String[] args) { String classPath = className.substring(0, className.lastIndexOf('/')); if (!classPaths.contains(classPath)) { classPaths.add(classPath); - out.println("${gwt.root}/" + classPath + "/package-info.java" + ":\\"); + out.println("${gwt.root}/" + classPath + "/package-info.java" + ",\\"); } if (i < USER_CLASSES.length - 1) { - out.println("${gwt.root}/" + className + ":\\"); + out.println("${gwt.root}/" + className + ",\\"); } else { out.println("${gwt.root}/" + className); } } - out.println(""); + out.println(); out.println("# Packages to include"); out.println("USER_PKGS=\\"); @@ -171,7 +180,7 @@ public static void main(String[] args) { for (int i = 0; i < packages.size(); i++) { if (i < packages.size() - 1) { - out.println(packageList.get(i) + ";\\"); + out.println(packageList.get(i) + ",\\"); } else { out.println(packageList.get(i)); } diff --git a/build_tools/doctool/src/com/google/doctool/custom/GWTJavaDoclet.java b/build_tools/doctool/src/com/google/doctool/custom/GWTJavaDoclet.java deleted file mode 100644 index ad9e747b3bd..00000000000 --- a/build_tools/doctool/src/com/google/doctool/custom/GWTJavaDoclet.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2006 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.doctool.custom; - -import com.sun.javadoc.Doclet; -import com.sun.javadoc.RootDoc; -import com.sun.tools.doclets.standard.Standard; -import com.sun.tools.javadoc.Main; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A doclet for using GWT-specific tags in standard javadoc output. - */ -public class GWTJavaDoclet extends Doclet { - - static RootDoc root = null; - - private static final String[] TAGLET_ARGS = new String[] { - "-taglet", ExampleTaglet.class.getName(), "-taglet", - TipTaglet.class.getName(), "-taglet", IncludeTaglet.class.getName()}; - - public static void main(String[] args) { - List examplePackages = new ArrayList(); - List filteredArgs = new ArrayList(); - - // filter out and save packages args - for (int i = 0; i < args.length; ++i) { - if (args[i].equalsIgnoreCase("-examplepackages")) { - String nextArg = args[++i]; - String[] split = nextArg.split(":|;"); - for (int j = 0; j < split.length; ++j) { - examplePackages.add(split[j]); - } - } else if (args[i].equalsIgnoreCase("-packages")) { - String nextArg = args[++i]; - String[] split = nextArg.split(":|;"); - for (int j = 0; j < split.length; ++j) { - filteredArgs.add(split[j]); - } - } else { - filteredArgs.add(args[i]); - } - } - - // Build a javadoc structure that includes example packages for reference - String name = GWTJavaDoclet.class.getName(); - List myArgs = new ArrayList(); - myArgs.addAll(filteredArgs); - myArgs.addAll(examplePackages); - Main.execute(name, name, (String[]) myArgs.toArray(new String[] {})); - - // Now delegate to the real javadoc without the example packages - filteredArgs.addAll(0, Arrays.asList(TAGLET_ARGS)); - Main.execute((String[]) filteredArgs.toArray(new String[] {})); - } - - public static int optionLength(String option) { - // delegate - return Standard.optionLength(option); - } - - public static boolean start(RootDoc root) { - // cache the root; ExampleTag will use it for reference later. - GWTJavaDoclet.root = root; - return true; - } - -} diff --git a/build_tools/doctool/src/com/google/doctool/custom/IncludeTaglet.java b/build_tools/doctool/src/com/google/doctool/custom/IncludeTaglet.java index 26e18bf9a51..80ac4443f39 100644 --- a/build_tools/doctool/src/com/google/doctool/custom/IncludeTaglet.java +++ b/build_tools/doctool/src/com/google/doctool/custom/IncludeTaglet.java @@ -17,65 +17,54 @@ import com.google.doctool.ResourceIncluder; -import com.sun.javadoc.Tag; -import com.sun.tools.doclets.Taglet; +import com.sun.source.doctree.DocTree; -import java.util.Map; +import javax.lang.model.element.Element; +import javax.tools.Diagnostic; +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; /** * A taglet for slurping in the content of artbitrary files appearing on the * classpath into javadoc. */ -public class IncludeTaglet implements Taglet { - - public static void register(Map tagletMap) { - IncludeTaglet tag = new IncludeTaglet(); - Taglet t = (Taglet) tagletMap.get(tag.getName()); - if (t != null) { - tagletMap.remove(tag.getName()); - } - tagletMap.put(tag.getName(), tag); - } +public class IncludeTaglet extends AbstractTaglet { + @Override public String getName() { return "gwt.include"; } - public boolean inConstructor() { - return true; - } - - public boolean inField() { - return true; - } - - public boolean inMethod() { - return true; - } - - public boolean inOverview() { - return true; + @Override + public String toString(List list, Element element) { + StringBuilder results = new StringBuilder(); + for (DocTree docTree : list) { + String text = getHtmlContent(docTree); + + try { + String contents = ResourceIncluder.getResourceFromClasspathScrubbedForHTML(text); + results.append("
").append(contents).append("
"); + } catch (IOException e) { + e.printStackTrace(); + printMessage(Diagnostic.Kind.ERROR, "Error in reading file: " + e.getMessage(), element, + docTree); + // return empty to let javadoc report this + return ""; + } + } + return results.toString(); } - public boolean inPackage() { - return true; - } - - public boolean inType() { - return true; + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(Location.class); } + @Override public boolean isInlineTag() { return true; } - public String toString(Tag tag) { - String contents = ResourceIncluder.getResourceFromClasspathScrubbedForHTML(tag); - return "
" + contents + "
"; - } - - public String toString(Tag[] tags) { - return null; - } - } diff --git a/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java b/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java new file mode 100644 index 00000000000..1b8a5ce21be --- /dev/null +++ b/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java @@ -0,0 +1,290 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.doctool.custom; + +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A doclet for listing the specified classes and + * their methods and constructors. + */ +public class JavaEmulSummaryDoclet implements Doclet { + + public static final String OPT_OUTFILE = "-outfile"; + private static final String JAVADOC_URL = "https://docs.oracle.com/en/java/javase/11/docs/api/"; + + private Reporter reporter; + private String outputFile; + + @Override + public boolean run(DocletEnvironment env) { + try { + + File outFile = new File(outputFile); + outFile.getParentFile().mkdirs(); + try (FileWriter fw = new FileWriter(outFile); + PrintWriter pw = new PrintWriter(fw, true)) { + + pw.println("
    "); + getSpecifiedPackages(env) + .forEach(pack -> { + pw.format("
  1. %s
  2. \n", + pack.getQualifiedName() + .toString().replace('.', '_'), + pack.getQualifiedName().toString()); + }); + + pw.println("
\n"); + + getSpecifiedPackages(env).forEach(pack -> { + pw.format("

Package %s

\n", + pack.getQualifiedName().toString().replace('.', '_'), + pack.getQualifiedName().toString()); + pw.println("
"); + + String packURL = JAVADOC_URL + pack.getQualifiedName().toString() + .replace(".", "/") + "/"; + + Iterator classesIterator = pack.getEnclosedElements() + .stream() + .filter(element -> env.isSelected(element) && env.isIncluded(element)) + .filter(element -> element.getModifiers().contains(Modifier.PUBLIC)) + .sorted(Comparator.comparing((Element o) -> o.getSimpleName() + .toString())) + .iterator(); + + while (classesIterator.hasNext()) { + Element cls = classesIterator.next(); + // Each class links to Oracle's main JavaDoc + emitClassDocs(env, pw, packURL, cls); + if (classesIterator.hasNext()) { + pw.print("\n"); + } + } + + pw.println("
\n"); + }); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return true; + } + + private void emitClassDocs(DocletEnvironment env, PrintWriter pw, String packURL, Element cls) { + pw.format("
%s
\n", packURL, + qualifiedSimpleName(cls), qualifiedSimpleName(cls)); + + // Print out all fields + String fields = cls.getEnclosedElements() + .stream() + .filter(element -> element.getKind().isField()) + .filter(field -> field.getModifiers().contains(Modifier.PUBLIC)) + .map(field -> field.getSimpleName().toString()) + .collect(Collectors.joining(", ")); + + if (!fields.isEmpty()) { + pw.format("
%s
\n", fields); + } + + List constructors = cls.getEnclosedElements() + .stream() + .filter(element -> ElementKind.CONSTRUCTOR == element.getKind()) + .filter(member -> member.getModifiers().contains(Modifier.PUBLIC)) + .map(member -> (ExecutableElement) member) + .map(executableElement -> flatSignature(env, cls, executableElement)) + .collect(Collectors.toList()); + + List methods = cls.getEnclosedElements() + .stream() + .filter(element -> ElementKind.METHOD == element.getKind()) + .filter(member -> member.getModifiers().contains(Modifier.PUBLIC)) + .map(member -> (ExecutableElement) member) + .map(executableElement -> flatSignature(env, cls, executableElement)) + .collect(Collectors.toList()); + + List members = new ArrayList<>(constructors); + members.addAll(methods); + + // Print out all constructors and methods + if (!members.isEmpty()) { + pw.format("
%s
\n", createMemberList(members)); + } + + Iterator classesIterator = cls.getEnclosedElements() + .stream() + .filter(element -> element.getKind().isClass() + || element.getKind().isInterface() + || ElementKind.ENUM == element.getKind()) + .filter(element -> element.getModifiers().contains(Modifier.PUBLIC)) + .sorted(Comparator.comparing((Element o) -> o.getSimpleName().toString())) + .iterator(); + if (classesIterator.hasNext()) { + pw.print("\n"); + } + while (classesIterator.hasNext()) { + Element innerCls = classesIterator.next(); + // Each class links to Sun's main JavaDoc + emitClassDocs(env, pw, packURL, innerCls); + if (classesIterator.hasNext()) { + pw.print("\n"); + } + } + } + + private String createMemberList(Collection members) { + StringBuilder sb = new StringBuilder(); + Iterator iter = members.iterator(); + while (iter.hasNext()) { + String member = iter.next(); + sb.append(member); + if (iter.hasNext()) { + sb.append(", "); + } + } + return sb.toString(); + } + + private String qualifiedSimpleName(Element element) { + String elementName = element.getSimpleName().toString(); + if (ElementKind.PACKAGE != element.getEnclosingElement().getKind()) { + return qualifiedSimpleName(element.getEnclosingElement()) + "." + elementName; + } + return elementName; + } + + private String flatSignature(DocletEnvironment env, Element parent, ExecutableElement member) { + return (ElementKind.CONSTRUCTOR == member.getKind() + ? parent.getSimpleName().toString() + : member.getSimpleName().toString()) + + "(" + member.getParameters() + .stream() + .map(Element::asType) + .map(t -> simpleParamName(env, t)) + .collect(Collectors.joining(", ")) + ")"; + } + + private String simpleParamName(DocletEnvironment env, TypeMirror type) { + if (type.getKind().isPrimitive() || TypeKind.TYPEVAR == type.getKind()) { + return String.valueOf(type); + } else if (TypeKind.ARRAY == type.getKind()) { + return simpleParamName(env, ((ArrayType) type).getComponentType()) + "[]"; + } else { + return qualifiedSimpleName(env.getTypeUtils().asElement(type)); + } + } + + @Override + public void init(Locale locale, Reporter reporter) { + this.reporter = reporter; + } + + @Override + public String getName() { + return "JreEmulationSummaryDoclet"; + } + + @Override + public Set getSupportedOptions() { + Option[] options = { + new Option() { + + @Override + public int getArgumentCount() { + return 1; + } + + @Override + public String getDescription() { + return "JRE emulation summary Doc location"; + } + + @Override + public Kind getKind() { + return Kind.STANDARD; + } + + @Override + public List getNames() { + return List.of(OPT_OUTFILE); + } + + @Override + public String getParameters() { + return "file"; + } + + @Override + public boolean process(String opt, List arguments) { + if (arguments.isEmpty()) { + reporter.print(Diagnostic.Kind.ERROR, + "You must specify an output filepath with " + + OPT_OUTFILE); + return false; + } + reporter.print(Diagnostic.Kind.NOTE, + "JRE emulation summary Doclet Option : " + + arguments.get(0)); + outputFile = arguments.get(0); + return true; + } + } + }; + return new HashSet<>(Arrays.asList(options)); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + private Stream getSpecifiedPackages(DocletEnvironment root) { + return root.getSpecifiedElements() + .stream() + .filter(element -> ElementKind.PACKAGE == element.getKind()) + .map(element -> (PackageElement) element); + } +} diff --git a/build_tools/doctool/src/com/google/doctool/custom/TipTaglet.java b/build_tools/doctool/src/com/google/doctool/custom/TipTaglet.java index a17e0b33210..830b23cb486 100644 --- a/build_tools/doctool/src/com/google/doctool/custom/TipTaglet.java +++ b/build_tools/doctool/src/com/google/doctool/custom/TipTaglet.java @@ -15,80 +15,52 @@ */ package com.google.doctool.custom; -import com.sun.javadoc.Tag; -import com.sun.tools.doclets.Taglet; +import com.sun.source.doctree.DocTree; -import java.util.Map; +import javax.lang.model.element.Element; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; /** * A taglet for including GWT tip tags in javadoc output. */ -public class TipTaglet implements Taglet { - - public static void register(Map tagletMap) { - TipTaglet tag = new TipTaglet(); - Taglet t = (Taglet) tagletMap.get(tag.getName()); - if (t != null) { - tagletMap.remove(tag.getName()); - } - tagletMap.put(tag.getName(), tag); - } - - public TipTaglet() { - } +public class TipTaglet extends AbstractTaglet { + @Override public String getName() { return "tip"; } - public boolean inConstructor() { - return true; - } - - public boolean inField() { - return true; - } - - public boolean inMethod() { - return true; - } - - public boolean inOverview() { - return true; - } - - public boolean inPackage() { - return true; + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(Location.class); } - public boolean inType() { - return true; - } - - public boolean isInlineTag() { - return false; - } - - public String toString(Tag tag) { - return null; - } - - public String toString(Tag[] tags) { - if (tags == null || tags.length == 0) { + @Override + public String toString(List list, Element element) { + if (list == null || list.size() == 0) { return null; } - String result = "
Tip:
"; - if (tags.length == 1) { - result += tags[0].text(); + StringBuilder result = new StringBuilder("
Tip:
"); + if (list.size() == 1) { + result.append(getHtmlContent(list.get(0))); } else { - result += "
    "; - for (int i = 0; i < tags.length; i++) { - result += "
  • " + tags[i].text() + "
  • "; + result.append("
      "); + for (int i = 0; i < list.size(); i++) { + result.append("
    • "); + result.append(getHtmlContent(list.get(i))); + result.append("
    • "); } - result += "
    "; + result.append("
"); } - result += "
"; - return result; + result.append(""); + return result.toString(); + } + + @Override + public boolean isInlineTag() { + return false; } } diff --git a/common.ant.xml b/common.ant.xml index 7c5d26ea669..36a56b4882d 100755 --- a/common.ant.xml +++ b/common.ant.xml @@ -28,11 +28,11 @@ - + + + @@ -62,8 +62,7 @@ - - + @@ -158,8 +157,7 @@ - - + @@ -172,7 +170,7 @@ @@ -251,6 +249,7 @@ + @@ -273,6 +272,7 @@ + diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java index d6b333a1e17..273e63cf4f2 100644 --- a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java +++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java @@ -30,7 +30,6 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.servlets.GzipFilter; import java.io.BufferedReader; import java.io.File; @@ -125,7 +124,7 @@ void start(final TreeLogger logger) throws UnableToCompleteException { connector.setReuseAddress(false); newServer.addConnector(connector); - ServletContextHandler newHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + ServletContextHandler newHandler = new ServletContextHandler(ServletContextHandler.GZIP); newHandler.setContextPath("/"); newHandler.addServlet(new ServletHolder(new HttpServlet() { @Override @@ -134,7 +133,6 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) handleRequest(request.getPathInfo(), request, response, logger); } }), "/*"); - newHandler.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); newServer.setHandler(newHandler); try { newServer.start(); diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js index 59fca5c907f..c9c39393862 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js @@ -46,8 +46,10 @@ function setupInstallLocation() { // throwing away the current document. // // In IE, it ensures that the element is immediately available. - frameDoc.open(); - var doctype = (document.compatMode == 'CSS1Compat') ? '' : ''; - frameDoc.write(doctype + ''); - frameDoc.close(); + if (navigator.userAgent.indexOf("Chrome") == -1) { + frameDoc.open(); + var doctype = (document.compatMode == 'CSS1Compat') ? '' : ''; + frameDoc.write(doctype + ''); + frameDoc.close(); + } } diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js index 725505e0bf0..2df43d26150 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js @@ -9,7 +9,9 @@ function installScript(filename) { var docbody = doc.body; var script = doc.createElement('script'); script.language='javascript'; - script.crossOrigin=''; + if (location.host) { + script.crossOrigin=''; + } script.src = code; if (__MODULE_FUNC__.__errFn) { script.onerror = function() { diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js index 0e2712b130d..85bd6e62090 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js @@ -19,29 +19,15 @@ function installScript(filename) { var doc = getInstallLocationDoc(); var docbody = doc.body; var script; - // for sourcemaps, we inject textNodes into the script element on Chrome - if (navigator.userAgent.indexOf("Chrome") > -1 && window.JSON) { - var scriptFrag = doc.createDocumentFragment() - // surround code with eval until crbug #90707 - scriptFrag.appendChild(doc.createTextNode("eval(\"")); - for (var i = 0; i < code.length; i++) { - // escape newlines, backslashes, and quotes with JSON.stringify - // rather than create multiple script tags which mess up line numbers, we use 1 tag, multiple text nodes - var c = window.JSON.stringify(code[i]); - // trim beginning/end quotes - scriptFrag.appendChild(doc.createTextNode(c.substring(1, c.length - 1))); - } - // close the eval - scriptFrag.appendChild(doc.createTextNode("\");")); + // for sourcemaps, we inject the code as a single string for Chrome + if (navigator.userAgent.indexOf("Chrome") > -1) { script = doc.createElement('script'); - script.language='javascript'; - script.appendChild(scriptFrag); + script.text = code.join(''); docbody.appendChild(script); removeScript(docbody, script); } else { for (var i = 0; i < code.length; i++) { script = doc.createElement('script'); - script.language='javascript'; script.text = code[i]; docbody.appendChild(script); removeScript(docbody, script); diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java index beada0d13e1..bfa7223d38b 100644 --- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java +++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java @@ -169,16 +169,13 @@ private void analyzeFragment(MemberFactory memberFactory, TreeSet c assert info != null; // Infer dependency information - if (!dependencyScope.isEmpty()) { - + while (!dependencyScope.isEmpty() && !dependencyScope.peek().range.contains(range)) { /* * Pop frames until we get back to a container, using this as a chance * to build up our list of non-overlapping Ranges to report back to the * user. */ - while (!dependencyScope.peek().range.contains(range)) { - popAndRecord(dependencyScope, fragment); - } + popAndRecord(dependencyScope, fragment); } // Possibly create and record Members @@ -271,7 +268,7 @@ private void emitStory(StoryImpl story, Range range) throws IOException { builder.append("\"/>\n\n"); } else { builder.append("\">"); - SizeMapRecorder.escapeXml(jsCode, start, end, false, builder); + SizeMapRecorder.escapeXml(jsCode, start, Math.min(end,jsCode.length()), false, builder); builder.append("\n\n"); } } @@ -297,7 +294,7 @@ private void popAndRecord(Stack dependencyScope, int fragment) throws * Make a new Range for the gap between the popped Range and whatever we * last stored. */ - if (lastEnd < toStore.getStart()) { + if (lastEnd < toStore.getStart() && !dependencyScope.isEmpty()) { Range newRange = new Range(lastEnd, toStore.getStart()); assert !dependencyScope.isEmpty(); @@ -351,7 +348,9 @@ private void recordStory(SourceInfo info, int fragment, int length, Range range) theStory = new StoryImpl(storyCache.get(info), length); } - emitStory(theStory, range); + if (range.getStart() < js[curHighestFragment].length()) { + emitStory(theStory, range); + } } } diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js b/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js index 53d168da3d6..30cfb179c3e 100644 --- a/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js +++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js @@ -93,15 +93,21 @@ function __MODULE_FUNC__() { ,markerId = "__gwt_marker___MODULE_NAME__" ,markerScript; - $doc.write(''); - markerScript = $doc.getElementById(markerId); - - // Our script element is assumed to be the closest previous script element - // to the marker, so start at the marker and walk backwards until we find - // a script. - thisScript = markerScript && markerScript.previousSibling; - while (thisScript && thisScript.tagName != 'SCRIPT') { - thisScript = thisScript.previousSibling; + if ($doc.currentScript) { + // document.currentScript is not supported by IE 11. + thisScript = $doc.currentScript; + } else { + // may fail in extension or anywhere else with inability to use document.write + $doc.write(''); + markerScript = $doc.getElementById(markerId); + + // Our script element is assumed to be the closest previous script element + // to the marker, so start at the marker and walk backwards until we find + // a script. + thisScript = markerScript && markerScript.previousSibling; + while (thisScript && thisScript.tagName != 'SCRIPT') { + thisScript = thisScript.previousSibling; + } } // Gets the part of a url up to and including the 'path' portion. diff --git a/dev/core/src/com/google/gwt/dev/javac/BytecodeSignatureMaker.java b/dev/core/src/com/google/gwt/dev/javac/BytecodeSignatureMaker.java index 762c4228ed8..a70120930ba 100644 --- a/dev/core/src/com/google/gwt/dev/javac/BytecodeSignatureMaker.java +++ b/dev/core/src/com/google/gwt/dev/javac/BytecodeSignatureMaker.java @@ -56,7 +56,7 @@ private static class CompileDependencyVisitor extends ClassVisitor { private Map methods = new HashMap(); public CompileDependencyVisitor() { - super(Opcodes.ASM7); + super(Opcodes.ASM9); } public String getSignature() { diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java index 6f7f003dc0d..0066c2f7628 100644 --- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java +++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java @@ -66,7 +66,7 @@ private static class AnonymousClassVisitor extends EmptyVisitor { public AnonymousClassVisitor() { - this.mv = new org.objectweb.asm.MethodVisitor(Opcodes.ASM7, this.mv) { + this.mv = new org.objectweb.asm.MethodVisitor(Opcodes.ASM9, this.mv) { @Override public void visitCode() { ++sawCode; diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java index 95fcc52fc0b..2d26d19d577 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java +++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java @@ -79,7 +79,7 @@ public static class MyAnnotationArrayVisitor extends AnnotationVisitor { private final List values = new ArrayList(); public MyAnnotationArrayVisitor(Callback callback) { - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.callback = callback; } @@ -158,7 +158,7 @@ public CollectAnnotationData(String desc, boolean visible) { */ CollectAnnotationData(String desc, boolean visible, Callback callback) { - super(Opcodes.ASM7); + super(Opcodes.ASM9); annotation = new AnnotationData(desc, visible); this.callback = callback; } diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java index 3bfb4348559..234e2865b70 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java +++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java @@ -36,7 +36,7 @@ public class CollectFieldData extends FieldVisitor { public CollectFieldData(int access, String name, String desc, String signature, Object value) { - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.access = access; this.name = name; this.desc = desc; diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java index 95f17a411fa..c4af9901f93 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java +++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java @@ -58,7 +58,7 @@ public class CollectMethodData extends MethodVisitor { // for new List[] public CollectMethodData(CollectClassData.ClassType classType, int access, String name, String desc, String signature, String[] exceptions) { - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.access = access; this.name = name; this.desc = desc; diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java index 17b32939782..773a4073933 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java +++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java @@ -42,7 +42,7 @@ public class CollectReferencesVisitor extends EmptyVisitor { private class CollectGenericTypes extends SignatureVisitor { public CollectGenericTypes() { - super(Opcodes.ASM7); + super(Opcodes.ASM9); } @Override @@ -121,7 +121,7 @@ public void visitTypeVariable(String name) { } CollectReferencesVisitor() { - this.av = new AnnotationVisitor(Opcodes.ASM7, this.av) { + this.av = new AnnotationVisitor(Opcodes.ASM9, this.av) { @Override public void visitEnum(String name, String desc, String value) { addTypeIfClass(desc); diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java index fe5385711ec..df82c2a2608 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java +++ b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java @@ -34,7 +34,7 @@ public class EmptySignatureVisitor extends SignatureVisitor { protected static EmptySignatureVisitor ignore = new EmptySignatureVisitor(); public EmptySignatureVisitor() { - super(Opcodes.ASM7); + super(Opcodes.ASM9); } /** diff --git a/dev/core/src/com/google/gwt/dev/javac/asmbridge/EmptyVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asmbridge/EmptyVisitor.java index ffa3ba4b9f9..7585c7def0b 100644 --- a/dev/core/src/com/google/gwt/dev/javac/asmbridge/EmptyVisitor.java +++ b/dev/core/src/com/google/gwt/dev/javac/asmbridge/EmptyVisitor.java @@ -27,7 +27,7 @@ */ public class EmptyVisitor extends ClassVisitor { - protected AnnotationVisitor av = new AnnotationVisitor(Opcodes.ASM7) { + protected AnnotationVisitor av = new AnnotationVisitor(Opcodes.ASM9) { @Override public AnnotationVisitor visitAnnotation(String name, String desc) { @@ -41,10 +41,10 @@ public AnnotationVisitor visitArray(String name) { }; public EmptyVisitor() { - super(Opcodes.ASM7); + super(Opcodes.ASM9); } - protected MethodVisitor mv = new MethodVisitor(Opcodes.ASM7) { + protected MethodVisitor mv = new MethodVisitor(Opcodes.ASM9) { @Override public AnnotationVisitor visitAnnotationDefault() { @@ -63,7 +63,7 @@ public AnnotationVisitor visitParameterAnnotation( } }; - protected FieldVisitor fv = new FieldVisitor(Opcodes.ASM7) { + protected FieldVisitor fv = new FieldVisitor(Opcodes.ASM9) { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java index c9a3077a198..53adcc5864f 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java @@ -731,15 +731,13 @@ public boolean visit(JPermutationDependentValue x, Context ctx) { print(".class)"); } else { assert x.isProperty(); - print("GWT.getProperty("); + print("System.getProperty("); print("\""); print(x.getRequestedValue()); print("\""); - if (x.getResultValues().get(0) != null) { - print(","); - print("\""); - print(x.getResultValues().get(0)); - print("\""); + if (x.getDefaultValueExpression() != null) { + print(", "); + accept(x.getDefaultValueExpression()); } print(")"); } diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java index 9c1475c027e..69b1dc9dd79 100644 --- a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java +++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java @@ -42,6 +42,10 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.preventers.AppContextLeakPreventer; +import org.eclipse.jetty.util.preventers.DOMLeakPreventer; +import org.eclipse.jetty.util.preventers.GCThreadLeakPreventer; +import org.eclipse.jetty.util.preventers.SecurityProviderLeakPreventer; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.ClasspathPattern; import org.eclipse.jetty.webapp.Configuration; @@ -60,10 +64,6 @@ import java.util.Iterator; import java.util.List; -import javax.imageio.ImageIO; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - /** * A {@link ServletContainerLauncher} for an embedded Jetty server. */ @@ -744,6 +744,7 @@ public ServletContainer start(TreeLogger logger, int port, File appRootDir) ServerConnector connector = getConnector(server, logger); setupConnector(connector, bindAddress, port); server.addConnector(connector); + addPreventers(server); Configuration.ClassList cl = Configuration.ClassList.setServerDefault(server); try { @@ -859,6 +860,37 @@ protected HttpConfiguration defaultConfig() { return config; } + private void addPreventers(Server server) { + // Trigger a call to sun.awt.AppContext.getAppContext(). This will + // pin the common class loader in memory but that shouldn't be an + // issue. + server.addBean(new AppContextLeakPreventer()); + + /* + * Several components end up calling: sun.misc.GC.requestLatency(long) + * + * Those libraries / components known to trigger memory leaks due to + * eventual calls to requestLatency(long) are: + * - javax.management.remote.rmi.RMIConnectorServer.start() + */ + server.addBean(new GCThreadLeakPreventer()); + + /* + * Creating a MessageDigest during web application startup initializes the + * Java Cryptography Architecture. Under certain conditions this starts a + * Token poller thread with TCCL equal to the web application class loader. + * + * Instead we initialize JCA right now. + */ + server.addBean(new SecurityProviderLeakPreventer()); + + /* + * Haven't got to the root of what is going on with this leak but if a web app is the first to + * make the calls below the web application class loader will be pinned in memory. + */ + server.addBean(new DOMLeakPreventer()); + } + private void checkStartParams(TreeLogger logger, int port, File appRootDir) { if (logger == null) { throw new NullPointerException("logger cannot be null"); @@ -899,37 +931,6 @@ private TreeLogger.Type getBaseLogLevel() { * (http://www.apache.org/). */ private void jreLeakPrevention(TreeLogger logger) { - // Trigger a call to sun.awt.AppContext.getAppContext(). This will - // pin the common class loader in memory but that shouldn't be an - // issue. - ImageIO.getCacheDirectory(); - - /* - * Several components end up calling: sun.misc.GC.requestLatency(long) - * - * Those libraries / components known to trigger memory leaks due to - * eventual calls to requestLatency(long) are: - - * javax.management.remote.rmi.RMIConnectorServer.start() - */ - try { - Class clazz = Class.forName("sun.misc.GC"); - Method method = clazz.getDeclaredMethod("requestLatency", - new Class[]{long.class}); - method.invoke(null, Long.valueOf(3600000)); - } catch (ClassNotFoundException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } catch (SecurityException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } catch (NoSuchMethodException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } catch (IllegalArgumentException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } catch (IllegalAccessException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } catch (InvocationTargetException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e); - } - /* * Calling getPolicy retains a static reference to the context class loader. */ @@ -953,15 +954,6 @@ private void jreLeakPrevention(TreeLogger logger) { logger.log(TreeLogger.WARN, "jreLeakPrevention.authPolicyFail", e); } - /* - * Creating a MessageDigest during web application startup initializes the - * Java Cryptography Architecture. Under certain conditions this starts a - * Token poller thread with TCCL equal to the web application class loader. - * - * Instead we initialize JCA right now. - */ - java.security.Security.getProviders(); - /* * Several components end up opening JarURLConnections without first * disabling caching. This effectively locks the file. Whilst more @@ -984,17 +976,5 @@ private void jreLeakPrevention(TreeLogger logger) { } catch (IOException e) { logger.log(TreeLogger.ERROR, "jreLeakPrevention.jarUrlConnCacheFail", e); } - - /* - * Haven't got to the root of what is going on with this leak but if a web - * app is the first to make the calls below the web application class loader - * will be pinned in memory. - */ - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - try { - factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - logger.log(TreeLogger.ERROR, "jreLeakPrevention.xmlParseFail", e); - } } } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/ForceClassVersion15.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/ForceClassVersion15.java index 52c5e7517f7..2f175877de7 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/ForceClassVersion15.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/ForceClassVersion15.java @@ -25,7 +25,7 @@ class ForceClassVersion15 extends ClassVisitor { public ForceClassVersion15(ClassVisitor v) { - super(Opcodes.ASM7, v); + super(Opcodes.ASM9, v); } @Override diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/HasAnnotation.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/HasAnnotation.java index 15f0d1586b2..6b7d18fdbb6 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/HasAnnotation.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/HasAnnotation.java @@ -54,7 +54,7 @@ public static boolean hasAnnotation(byte[] classBytes, private final String targetDesc; public HasAnnotation(ClassVisitor v, Class annotation) { - super(Opcodes.ASM7, v); + super(Opcodes.ASM9, v); targetDesc = Type.getDescriptor(annotation); } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java index 6ce96eb0a17..29b801593a4 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java @@ -206,7 +206,7 @@ private class MyMethodAdapter extends GeneratorAdapter { public MyMethodAdapter(MethodVisitor mv, int access, String name, String desc) { - super(Opcodes.ASM7, mv, access, name, desc); + super(Opcodes.ASM9, mv, access, name, desc); this.descriptor = desc; this.name = name; isStatic = (access & Opcodes.ACC_STATIC) != 0; @@ -329,7 +329,7 @@ private void loadClassArray() { public RewriteJsniMethods(ClassVisitor v, Map anonymousClassMap) { - super(Opcodes.ASM7, v); + super(Opcodes.ASM9, v); this.anonymousClassMap = anonymousClassMap; } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java index 1911d517dcd..6ddf8241ca9 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java @@ -54,7 +54,7 @@ public String map(String typeName) { }; public MyMethodAdapter(MethodVisitor mv) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); } @Override @@ -132,7 +132,7 @@ public void visitTypeInsn(int opcode, String type) { */ public RewriteRefsToJsoClasses(ClassVisitor cv, Set jsoDescriptors, InstanceMethodOracle mapper) { - super(Opcodes.ASM7, cv); + super(Opcodes.ASM9, cv); this.jsoDescriptors = jsoDescriptors; this.mapper = mapper; } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java index ff38213b8d8..cd7eb1cf672 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java @@ -57,7 +57,7 @@ public class RewriteSingleJsoImplDispatches extends ClassVisitor { private class MyMethodVisitor extends MethodVisitor { public MyMethodVisitor(MethodVisitor mv) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); } /* @@ -133,7 +133,7 @@ public void visitMethodInsn(int opcode, String owner, String name, public RewriteSingleJsoImplDispatches(ClassVisitor v, TypeOracle typeOracle, SingleJsoImplData jsoData) { - super(Opcodes.ASM7, v); + super(Opcodes.ASM9, v); this.typeOracle = typeOracle; this.jsoData = jsoData; } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/UseMirroredClasses.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/UseMirroredClasses.java index 51ac42d387b..4ddbe63a52e 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/UseMirroredClasses.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/UseMirroredClasses.java @@ -72,7 +72,7 @@ private static class MethodInterceptor extends MethodVisitor { private String className; protected MethodInterceptor(MethodVisitor mv, String className) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); this.className = className; } @@ -144,7 +144,7 @@ public void visitMethodInsn(int opcode, String owner, String name, private String className; public UseMirroredClasses(ClassVisitor cv, String className) { - super(Opcodes.ASM7, cv); + super(Opcodes.ASM9, cv); this.className = className; } diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java index 8d460737ced..29956a17f04 100644 --- a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java +++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java @@ -264,7 +264,7 @@ public static ClassVisitor create(ClassVisitor cv, String classDescriptor, * @param mapper maps methods to the class in which they are declared */ private WriteJsoImpl(ClassVisitor cv, InstanceMethodOracle mapper) { - super(Opcodes.ASM7, cv); + super(Opcodes.ASM9, cv); this.mapper = mapper; } diff --git a/dev/core/src/com/google/gwt/dev/util/arg/SourceLevel.java b/dev/core/src/com/google/gwt/dev/util/arg/SourceLevel.java index d78d5739b84..d07680b3e1b 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/SourceLevel.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/SourceLevel.java @@ -24,9 +24,9 @@ public enum SourceLevel { // Source levels must appear in ascending order for the default setting logic to work. JAVA8("1.8", "8"), - JAVA9("1.9", "9"), - JAVA10("1.10", "10"), - JAVA11("1.11", "11"); + JAVA9("9", "1.9"), + JAVA10("10", "1.10"), + JAVA11("11", "1.11"); /** * The default java sourceLevel. @@ -85,7 +85,7 @@ private static SourceLevel getJvmBestMatchingSourceLevel() { @VisibleForTesting public static SourceLevel getBestMatchingVersion(String javaVersionString) { try { - // Find the first version that is less than or equal to javaSpecLevel by iterating in reverse + // Find the last version that is less than or equal to javaSpecLevel by iterating in reverse // order. SourceLevel[] sourceLevels = SourceLevel.values(); for (int i = sourceLevels.length - 1; i >= 0; i--) { diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java index 239a2f9b54d..0176bb029c7 100644 --- a/dev/core/test/com/google/gwt/dev/CompilerTest.java +++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java @@ -957,9 +957,9 @@ public void testSourceLevelSelection() { assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.7")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.8")); - assertEquals(SourceLevel.JAVA9, SourceLevel.getBestMatchingVersion("1.9")); - assertEquals(SourceLevel.JAVA10, SourceLevel.getBestMatchingVersion("1.10")); - assertEquals(SourceLevel.JAVA11, SourceLevel.getBestMatchingVersion("1.11")); + assertEquals(SourceLevel.JAVA9, SourceLevel.getBestMatchingVersion("9")); + assertEquals(SourceLevel.JAVA10, SourceLevel.getBestMatchingVersion("10")); + assertEquals(SourceLevel.JAVA11, SourceLevel.getBestMatchingVersion("11")); // not proper version strings => default to JAVA8. assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.6u3")); diff --git a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java index 11aae8bdedf..420d3da3608 100644 --- a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java +++ b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java @@ -18,11 +18,14 @@ import com.google.gwt.dev.jjs.ast.JTypeOracle; import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap; import com.google.gwt.thirdparty.guava.common.collect.Sets; -import com.google.gwt.thirdparty.guava.common.io.Files; import junit.framework.TestCase; -import java.io.File; +import org.apache.commons.io.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Map; /** @@ -30,15 +33,15 @@ */ public class MinimalRebuildCacheManagerTest extends TestCase { - public void testCacheChange() throws InterruptedException { + public void testCacheChange() throws InterruptedException, IOException { String moduleName = "com.google.FooModule"; Map initialCompilerOptions = ImmutableMap.of("option", "oldvalue"); PermutationDescription permutationDescription = new PermutationDescription(); - File cacheDir = Files.createTempDir(); + Path cacheDir = Files.createTempDirectory(null); MinimalRebuildCacheManager minimalRebuildCacheManager = new MinimalRebuildCacheManager( - TreeLogger.NULL, cacheDir, initialCompilerOptions); + TreeLogger.NULL, cacheDir.toFile(), initialCompilerOptions); // Make sure we start with a blank slate. minimalRebuildCacheManager.deleteCaches(); @@ -65,7 +68,7 @@ public void testCacheChange() throws InterruptedException { Map newCompilerOptions = ImmutableMap.of("option", "newvalue"); minimalRebuildCacheManager = new MinimalRebuildCacheManager( - TreeLogger.NULL, cacheDir, newCompilerOptions); + TreeLogger.NULL, cacheDir.toFile(), newCompilerOptions); // Now get the cache for FooModule under different compiler flags MinimalRebuildCache fooCacheNew = minimalRebuildCacheManager.getCache(moduleName, @@ -84,7 +87,7 @@ public void testCacheChange() throws InterruptedException { // Switch back to the initial option values and verify you get the same old cache. minimalRebuildCacheManager = new MinimalRebuildCacheManager( - TreeLogger.NULL, cacheDir, initialCompilerOptions); + TreeLogger.NULL, cacheDir.toFile(), initialCompilerOptions); // Now get the cache for FooModule under different under initial options values. MinimalRebuildCache fooCacheResetOptions = minimalRebuildCacheManager.getCache( @@ -96,16 +99,16 @@ public void testCacheChange() throws InterruptedException { assertTrue(fooCacheOld.hasSameContent(fooCacheResetOptions)); minimalRebuildCacheManager.deleteCaches(); minimalRebuildCacheManager.shutdown(); - cacheDir.delete(); + FileUtils.deleteDirectory(cacheDir.toFile()); } - public void testReload() throws InterruptedException { - File cacheDir = Files.createTempDir(); + public void testReload() throws InterruptedException, IOException { + Path cacheDir = Files.createTempDirectory(null); String moduleName = "com.google.FooModule"; MinimalRebuildCacheManager minimalRebuildCacheManager = new MinimalRebuildCacheManager( - TreeLogger.NULL, cacheDir, ImmutableMap.of()); + TreeLogger.NULL, cacheDir.toFile(), ImmutableMap.of()); PermutationDescription permutationDescription = new PermutationDescription(); // Make sure we start with a blank slate. @@ -149,7 +152,7 @@ public void testReload() throws InterruptedException { // Start a new cache manager in the same folder. MinimalRebuildCacheManager reloadedMinimalRebuildCacheManager = new MinimalRebuildCacheManager( - TreeLogger.NULL, cacheDir, ImmutableMap.of()); + TreeLogger.NULL, cacheDir.toFile(), ImmutableMap.of()); // Reread the previously saved cache. MinimalRebuildCache reloadedCache = diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java index 3843f2394df..382df33b24d 100644 --- a/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java +++ b/dev/core/test/com/google/gwt/dev/resource/impl/ClassPathEntryTest.java @@ -17,12 +17,13 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.thirdparty.guava.common.collect.Lists; -import com.google.gwt.thirdparty.guava.common.io.Files; -import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.DosFileAttributeView; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -32,21 +33,22 @@ */ public class ClassPathEntryTest extends AbstractResourceOrientedTestBase { - /** - * This test will likely not work on Windows since directories that start with . are not - * implicitly hidden there. But since Java 6 does not have a File.setHidden() function, fixing - * this test for Windows is deferred till GWT officially depends on Java 7. - */ public void testIgnoresHiddenDirectories() throws IOException { // Setup a /tmp/.svn/ShouldNotBeFound.java folder structure. - File tempDir = Files.createTempDir(); - File nestedHiddenDir = new File(tempDir, ".svn"); - nestedHiddenDir.mkdir(); - File javaFile = new File(nestedHiddenDir, "ShouldNotBeFound.java"); - javaFile.createNewFile(); + Path tempDir = Files.createTempDirectory(null); + Path nestedHiddenDir = tempDir.resolve(".svn"); + Files.createDirectory(nestedHiddenDir); + Path javaFile = nestedHiddenDir.resolve("ShouldNotBeFound.java"); + Files.createFile(javaFile); + // windows needs the hidden attribute to be set on file (not parent directory) + DosFileAttributeView hiddenAttr = Files.getFileAttributeView(javaFile, + DosFileAttributeView.class); + if (hiddenAttr != null) { + hiddenAttr.setHidden(true); + } // Perform a class path directory inspection. - DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir.toFile()); Map resources = cpe.findApplicableResources(TreeLogger.NULL, createInclusivePathPrefixSet()); @@ -66,7 +68,7 @@ public void testResourceCreated() throws IOException, InterruptedException { public void testForResourceListenerLeaks_pathPrefixSetIsCollected() throws Exception { // Create a folder an initially empty folder. PathPrefixSet pathPrefixSet = createInclusivePathPrefixSet(); - DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry(Files.createTempDir()); + DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry(Files.createTempDirectory(null).toFile()); // Show that we are not listening. awaitFullGc(); @@ -94,7 +96,8 @@ public void testForResourceListenerLeaks_pathPrefixSetIsCollected() throws Excep public void testForResourceListenerLeaks_classPathEntryIsCollected() throws Exception { // Create a folder an initially empty folder. PathPrefixSet pathPrefixSet = createInclusivePathPrefixSet(); - DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry(Files.createTempDir()); + DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry( + Files.createTempDirectory(null).toFile()); // Show that we are not listening. awaitFullGc(); @@ -122,8 +125,8 @@ public void testForResourceListenerLeaks_classPathEntryIsCollected() throws Exce public void testResourceCreated(Collection pathPrefixSets) throws IOException, InterruptedException { // Create a folder an initially empty folder. - File tempDir = Files.createTempDir(); - DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); + Path tempDir = Files.createTempDirectory(null); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir.toFile()); // Perform a class path directory inspection. for (PathPrefixSet pathPrefixSet : pathPrefixSets) { @@ -135,8 +138,8 @@ public void testResourceCreated(Collection pathPrefixSets) throws } // Create a file and give file events time to fire. - File createdFile = new File(tempDir, "Created.java"); - createdFile.createNewFile(); + Path createdFile = tempDir.resolve("Created.java"); + Files.createFile(createdFile); Thread.sleep(10); // Perform a class path directory inspection. @@ -161,10 +164,10 @@ public void testResourceDeleted() throws IOException, InterruptedException { private void testResourceDeleted(Collection pathPrefixSets) throws IOException, InterruptedException { // Create a folder with one initial file, that can be deleted. - File tempDir = Files.createTempDir(); - File fileToDelete = new File(tempDir, "ToDelete.java"); - fileToDelete.createNewFile(); - DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); + Path tempDir = Files.createTempDirectory(null); + Path fileToDelete = tempDir.resolve("ToDelete.java"); + Files.createFile(fileToDelete); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir.toFile()); // Perform a class path directory inspection. for (PathPrefixSet pathPrefixSet : pathPrefixSets) { @@ -177,7 +180,7 @@ private void testResourceDeleted(Collection pathPrefixSets) throw } // Delete the file and give file events time to fire. - fileToDelete.delete(); + Files.delete(fileToDelete); Thread.sleep(10); // Perform a class path directory inspection. @@ -201,10 +204,10 @@ public void testResourceRenamed() throws IOException, InterruptedException { private void testResourceRenamed(Collection pathPrefixSets) throws IOException, InterruptedException { // Create a folder with one initial file, that can be renamed. - File tempDir = Files.createTempDir(); - File fileToRename = new File(tempDir, "ToRename.java"); - fileToRename.createNewFile(); - DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); + Path tempDir = Files.createTempDirectory(null); + Path fileToRename = tempDir.resolve("ToRename.java"); + Files.createFile(fileToRename); + DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir.toFile()); // Perform class path directory inspections. for (PathPrefixSet pathPrefixSet : pathPrefixSets) { @@ -217,7 +220,7 @@ private void testResourceRenamed(Collection pathPrefixSets) throw } // Rename the file and give file events time to fire. - fileToRename.renameTo(new File(tempDir, "Renamed.java")); + Files.move(fileToRename, tempDir.resolve("Renamed.java")); Thread.sleep(10); for (PathPrefixSet pathPrefixSet : pathPrefixSets) { diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceAccumulatorTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceAccumulatorTest.java index ca5e3a9cd57..d61b8ba44fb 100644 --- a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceAccumulatorTest.java +++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceAccumulatorTest.java @@ -15,13 +15,15 @@ import com.google.gwt.thirdparty.guava.common.collect.Lists; import com.google.gwt.thirdparty.guava.common.collect.Sets; -import com.google.gwt.thirdparty.guava.common.io.Files; import junit.framework.TestCase; -import java.io.File; +import org.apache.commons.lang3.SystemUtils; + import java.io.IOException; import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -33,11 +35,11 @@ public class ResourceAccumulatorTest extends TestCase { public void testAddFile() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = createDirectoryIn("subdir", rootDirectory); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = createDirectoryIn("subdir", rootDirectory); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); assertTrue(getResources(resourceAccumulator).isEmpty()); @@ -52,18 +54,18 @@ public void testAddFile() throws Exception { } public void testDeleteFile() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = createDirectoryIn("subdir", rootDirectory); - File originalFile = createFileIn("SomeFile.java", subDirectory); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = createDirectoryIn("subdir", rootDirectory); + Path originalFile = createFileIn("SomeFile.java", subDirectory); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); List resources = getResources(resourceAccumulator); assertEquals(1, resources.size()); assertTrue(resources.get(0).getPath().endsWith("SomeFile.java")); - originalFile.delete(); + Files.delete(originalFile); waitForFileEvents(); assertTrue(getResources(resourceAccumulator).isEmpty()); @@ -72,16 +74,16 @@ public void testDeleteFile() throws Exception { } public void testListensInNewDirectories() throws Exception { - File rootDirectory = Files.createTempDir(); + Path rootDirectory = Files.createTempDirectory(null); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); assertTrue(getResources(resourceAccumulator).isEmpty()); // Create a new directory and contained file AFTER the root directory has started being listened // to. - File subDirectory = createDirectoryIn("subdir", rootDirectory); + Path subDirectory = createDirectoryIn("subdir", rootDirectory); createFileIn("New.java", subDirectory); waitForFileEvents(); @@ -93,13 +95,13 @@ public void testListensInNewDirectories() throws Exception { } public void testMultipleListeners() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = createDirectoryIn("subdir", rootDirectory); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = createDirectoryIn("subdir", rootDirectory); ResourceAccumulator resourceAccumulator1 = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); ResourceAccumulator resourceAccumulator2 = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); assertTrue(getResources(resourceAccumulator1).isEmpty()); assertTrue(getResources(resourceAccumulator2).isEmpty()); @@ -120,19 +122,19 @@ public void testMultipleListeners() throws Exception { } public void testRenameFile() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = createDirectoryIn("subdir", rootDirectory); - File originalFile = createFileIn("OriginalName.java", subDirectory); - File renamedFile = new File(subDirectory, "Renamed.java"); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = createDirectoryIn("subdir", rootDirectory); + Path originalFile = createFileIn("OriginalName.java", subDirectory); + Path renamedFile = subDirectory.resolve("Renamed.java"); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); List resources = getResources(resourceAccumulator); assertEquals(1, resources.size()); assertTrue(resources.get(0).getPath().endsWith("OriginalName.java")); - originalFile.renameTo(renamedFile); + Files.move(originalFile, renamedFile); waitForFileEvents(); resources = getResources(resourceAccumulator); @@ -143,21 +145,21 @@ public void testRenameFile() throws Exception { } public void testRenameDirectory() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = createDirectoryIn("original_dir", rootDirectory); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = createDirectoryIn("original_dir", rootDirectory); createFileIn("Name1.java", subDirectory); createFileIn("Name2.java", subDirectory); - File renamedSubDirectory = new File(rootDirectory, "new_dir"); + Path renamedSubDirectory = rootDirectory.resolve("new_dir"); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); List resources = getResources(resourceAccumulator); assertEquals(2, resources.size()); assertTrue(resources.get(0).getPath().endsWith("original_dir/Name1.java")); assertTrue(resources.get(1).getPath().endsWith("original_dir/Name2.java")); - subDirectory.renameTo(renamedSubDirectory); + Files.move(subDirectory, renamedSubDirectory); waitForFileEvents(); resources = getResources(resourceAccumulator); @@ -169,22 +171,25 @@ public void testRenameDirectory() throws Exception { } public void testRenameParentDirectory() throws Exception { - File rootDirectory = Files.createTempDir(); - File parentDirectory = createDirectoryIn("original_dir", rootDirectory); - File subDirectory = createDirectoryIn("subdir", parentDirectory); + if (SystemUtils.IS_OS_WINDOWS) { + return; // moving a directory while WatchService is running -> access denied + } + Path rootDirectory = Files.createTempDirectory(null); + Path parentDirectory = createDirectoryIn("original_dir", rootDirectory); + Path subDirectory = createDirectoryIn("subdir", parentDirectory); createFileIn("Name1.java", subDirectory); createFileIn("Name2.java", subDirectory); - File renamedParentDirectory = new File(rootDirectory, "new_dir"); + Path renamedParentDirectory = rootDirectory.resolve("new_dir"); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); List resources = getResources(resourceAccumulator); assertEquals(2, resources.size()); assertTrue(resources.get(0).getPath().endsWith("original_dir/subdir/Name1.java")); assertTrue(resources.get(1).getPath().endsWith("original_dir/subdir/Name2.java")); - parentDirectory.renameTo(renamedParentDirectory); + Files.move(parentDirectory, renamedParentDirectory); waitForFileEvents(); resources = getResources(resourceAccumulator); @@ -196,19 +201,22 @@ public void testRenameParentDirectory() throws Exception { } public void testSymlinkInfiniteLoop() throws Exception { - File rootDirectory = Files.createTempDir(); - File subDirectory = Files.createTempDir(); + if (SystemUtils.IS_OS_WINDOWS) { + return; // symlinks not working on Windows + } + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = Files.createTempDirectory(null); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); assertTrue(getResources(resourceAccumulator).isEmpty()); // Symlink in a loop - java.nio.file.Files.createSymbolicLink(new File(rootDirectory, "sublink").toPath(), - subDirectory.toPath()).toFile(); - java.nio.file.Files.createSymbolicLink(new File(subDirectory, "sublink").toPath(), - rootDirectory.toPath()).toFile(); + java.nio.file.Files.createSymbolicLink(rootDirectory.resolve("sublink"), + subDirectory); + java.nio.file.Files.createSymbolicLink(subDirectory.resolve("sublink"), + rootDirectory); createFileIn("New.java", subDirectory); waitForFileEvents(); @@ -224,21 +232,24 @@ public void testSymlinkInfiniteLoop() throws Exception { } public void testSymlinks() throws Exception { - File scratchDirectory = Files.createTempDir(); - File newFile = createFileIn("New.java", scratchDirectory); - File rootDirectory = Files.createTempDir(); - File subDirectory = Files.createTempDir(); + if (SystemUtils.IS_OS_WINDOWS) { + return; // symlinks not working on Windows + } + Path scratchDirectory = Files.createTempDirectory(null); + Path newFile = createFileIn("New.java", scratchDirectory); + Path rootDirectory = Files.createTempDirectory(null); + Path subDirectory = Files.createTempDirectory(null); ResourceAccumulator resourceAccumulator = - new ResourceAccumulator(rootDirectory.toPath(), createInclusivePathPrefixSet()); + new ResourceAccumulator(rootDirectory, createInclusivePathPrefixSet()); assertTrue(getResources(resourceAccumulator).isEmpty()); // Symlink in a subdirectory and then symlink in a contained file. - java.nio.file.Files.createSymbolicLink(new File(rootDirectory, "sublink").toPath(), - subDirectory.toPath()).toFile(); - java.nio.file.Files.createSymbolicLink(new File(subDirectory, "New.java").toPath(), - newFile.toPath()).toFile(); + Files.createSymbolicLink(rootDirectory.resolve("sublink"), + subDirectory); + Files.createSymbolicLink(subDirectory.resolve("New.java"), + newFile); waitForFileEvents(); List resources = getResources(resourceAccumulator); @@ -248,16 +259,14 @@ public void testSymlinks() throws Exception { resourceAccumulator.shutdown(); } - private static File createDirectoryIn(String fileName, File inDirectory) { - File newDirectory = new File(inDirectory, fileName); - newDirectory.mkdir(); - return newDirectory; + private static Path createDirectoryIn(String fileName, Path inDirectory) throws IOException { + Path newDirectory = inDirectory.resolve(fileName); + return Files.createDirectory(newDirectory); } - private static File createFileIn(String fileName, File inDirectory) throws IOException { - File newFile = new File(inDirectory, fileName); - newFile.createNewFile(); - return newFile; + private static Path createFileIn(String fileName, Path inDirectory) throws IOException { + Path newFile = inDirectory.resolve(fileName); + return Files.createFile(newFile); } private List getResources(ResourceAccumulator resourceAccumulator) @@ -284,7 +293,7 @@ private PathPrefixSet createInclusivePathPrefixSet() { PathPrefixSet pathPrefixSet = new PathPrefixSet(); pathPrefixSet.add(new PathPrefix("", null)); - // Keep the the reference until the end of the test to create a strong reference, otherwise + // Keep the reference until the end of the test to create a strong reference, otherwise // will get GCed as ResourceAccumulator refers to it weakly. pathPrefixes.add(pathPrefixSet); return pathPrefixSet; diff --git a/dev/core/test/org/apache/commons/collections/collection/AbstractTestCollection.java b/dev/core/test/org/apache/commons/collections/collection/AbstractTestCollection.java index d6e1485a1bc..0de0fe304da 100644 --- a/dev/core/test/org/apache/commons/collections/collection/AbstractTestCollection.java +++ b/dev/core/test/org/apache/commons/collections/collection/AbstractTestCollection.java @@ -1120,7 +1120,7 @@ public void testCollectionToArray2() { verify(); try { - array = collection.toArray(null); + array = collection.toArray((Object[]) null); fail("toArray(null) should raise NPE"); } catch (NullPointerException e) { // expected diff --git a/doc/build.xml b/doc/build.xml index 8bd05b64377..10050f29e37 100644 --- a/doc/build.xml +++ b/doc/build.xml @@ -3,8 +3,6 @@ - - @@ -34,7 +32,6 @@ - @@ -74,47 +71,41 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + @@ -125,24 +116,25 @@ - Building JRE emulation EZT + Building JRE emulation summary - - + + - - - - - - - + - + diff --git a/maven/README.txt b/maven/README.txt index 25bb9a5c6c2..17adaf94b9d 100644 --- a/maven/README.txt +++ b/maven/README.txt @@ -1,11 +1,11 @@ push-gwt.sh packages and deploys GWT to a local or remote Maven repository -To build GWT from trunk and deploy to your local repo: +To build GWT from main and deploy to your local repo: -> cd trunk +> git switch main > ant clean dist-dev -> maven/push-gwt.sh +> maven/push-gwtproject.sh Follow the prompts to set the GWT version # and path to the distribution ZIP (it will automatically find it in build/dist if @@ -19,9 +19,9 @@ script. If the remote repo requires a username and password, define the repo in your ~/.m2/settings.xml as below and set GWT_MAVEN_REPO_ID = server id. In this example, GWT_MAVEN_REPO_ID would be "sonatype". -> cd trunk +> git switch main > ant clean dist # must be dist, not dist-dev, to generate Javadocs -> maven/push-gwt.sh +> maven/push-gwtproject.sh ~/.m2/settings.xml: diff --git a/user/build.xml b/user/build.xml index 1c5aa2e3c72..df5fc64d077 100755 --- a/user/build.xml +++ b/user/build.xml @@ -15,18 +15,24 @@ - + value="**/*JreSuite.class,**/OptimizedOnly*" + else="**/*JreSuite.class,**/OptimizedOnly*,**/*Java9Suite.class,**/*Java10Suite.class,**/*Java11Suite.class"> + + + + + + + + - + value="**/EmulSuite.class,**/JSONSuite.class,**/RunAsyncSuite.class,**/*CompilerSuite.class,**/*JsInteropSuite.class,**/*JreSuite.class,**/OptimizedOnly*" + else="**/EmulSuite.class,**/JSONSuite.class,**/RunAsyncSuite.class,**/*CompilerSuite.class,**/*JsInteropSuite.class,**/*JreSuite.class,**/OptimizedOnly*,**/*Java9Suite.class,**/*Java10Suite.class,**/*Java11Suite.class"> @@ -67,8 +73,8 @@ - - + + - + + + + + + + + + + + + + diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/EnumMap_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/EnumMap_CustomFieldSerializer.java index b25b5099ad9..e2be8558a47 100644 --- a/user/src/com/google/gwt/user/client/rpc/core/java/util/EnumMap_CustomFieldSerializer.java +++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/EnumMap_CustomFieldSerializer.java @@ -19,9 +19,9 @@ import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; +import com.google.gwt.user.server.rpc.EnumMapUtil; -import java.lang.reflect.Array; -import java.lang.reflect.Field; +import java.io.IOException; import java.util.EnumMap; /** @@ -55,24 +55,13 @@ public static EnumMap instantiate(SerializationStreamReader streamReader) */ public static void serialize(SerializationStreamWriter streamWriter, EnumMap instance) throws SerializationException { - Class c = instance.getClass(); - Field keyUniverseField; - Object keyUniverse = null; - + Class> keyType; try { - keyUniverseField = c.getDeclaredField("keyUniverse"); - keyUniverseField.setAccessible(true); - keyUniverse = keyUniverseField.get(instance); - } catch (IllegalArgumentException e) { - throw new SerializationException(e); - } catch (IllegalAccessException e) { - throw new SerializationException(e); - } catch (SecurityException e) { - throw new SerializationException(e); - } catch (NoSuchFieldException e) { + keyType = EnumMapUtil.getKeyType(instance); + } catch (ClassNotFoundException | IOException e) { throw new SerializationException(e); } - Object exemplar = Array.get(keyUniverse, 0); + Object exemplar = keyType.getEnumConstants()[0]; streamWriter.writeObject(exemplar); Map_CustomFieldSerializerBase.serialize(streamWriter, instance); } diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java index 440bfca1f17..42b7c3f7522 100644 --- a/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java +++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java @@ -20,7 +20,10 @@ import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -105,15 +108,31 @@ private static boolean getAccessOrder(LinkedHashMap instance) { if (!reflectionHasFailed.get()) { try { Field f = accessOrderField.get(); - if (f == null || !f.isAccessible()) { + boolean isAccessible = f != null && f.isAccessible(); + if (!isAccessible) { f = LinkedHashMap.class.getDeclaredField("accessOrder"); synchronized (f) { - // Ensure all threads can see the accessibility. - f.setAccessible(true); + try { + // see if we can *try* setting the accessOrder field accessible: + final Method trySetAccessible = AccessibleObject.class.getDeclaredMethod( + "trySetAccessible"); + // no exception, then we are on Java 9 or beyond + if ((boolean) trySetAccessible.invoke(f)) { + accessOrderField.set(f); + isAccessible = true; + } + } catch (NoSuchMethodException e) { + // Java <= 8: it won't create a warning but it may fail with an exception + // Ensure all threads can see the accessibility. + f.setAccessible(true); + accessOrderField.set(f); + isAccessible = true; + } } - accessOrderField.set(f); } - return ((Boolean) f.get(instance)).booleanValue(); + if (isAccessible) { + return ((Boolean) f.get(instance)).booleanValue(); + } } catch (SecurityException e) { // fall through } catch (NoSuchFieldException e) { @@ -122,6 +141,8 @@ private static boolean getAccessOrder(LinkedHashMap instance) { // fall through } catch (IllegalAccessException e) { // fall through + } catch (InvocationTargetException e) { + // fall through } reflectionHasFailed.set(true); } diff --git a/user/src/com/google/gwt/user/server/rpc/EnumMap.java b/user/src/com/google/gwt/user/server/rpc/EnumMap.java new file mode 100644 index 00000000000..37744ea69fd --- /dev/null +++ b/user/src/com/google/gwt/user/server/rpc/EnumMap.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.user.server.rpc; + +/** + * A serialization-compatible mock for {@link java.util.EnumMap} that uses an equal + * {@link #serialVersionUID} and a compatible set of fields. When de-serializing an + * {@link java.util.EnumMap} instance into an instance of this class, e.g., by overriding + * {@link ObjectInputStream}'s {@link ObjectInputStream#resolveClass} method such that it delivers + * this class instead of {@link java.util.EnumMap}, the fields are made accessible through getters; + * in particular, {@link #getKeyType()} reveals the original {@link java.util.EnumMap}'s key type, + * even if the map is empty. + *

+ * + * The {@link EnumMap#getEnumMapKeyType(java.util.EnumMap)} method can be used to determine the key + * type of any {@link java.util.EnumMap}, even if it's empty. + */ +class EnumMap, V> extends java.util.EnumMap { + private final Class keyType; + private transient K[] keyUniverse; + private transient Object[] vals; + private transient int size = 0; + private static final long serialVersionUID = 458661240069192865L; + + EnumMap(Class c) { + super(c); + keyType = null; + } + + public K[] getKeyUniverse() { + return keyUniverse; + } + + public Object[] getVals() { + return vals; + } + + public int getSize() { + return size; + } + + public Class getKeyType() { + return keyType; + } + + /** + * Reconstitute the EnumMap instance from a stream (i.e., deserialize it). + */ + private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, + ClassNotFoundException { + // Read in the key type and any hidden stuff + s.defaultReadObject(); + // Read in size (number of Mappings) + int numberOfMappings = s.readInt(); + // Read the keys and values, and put the mappings in the HashMap + for (int i = 0; i < numberOfMappings; i++) { + s.readObject(); // key + s.readObject(); // value + } + } +} diff --git a/user/src/com/google/gwt/user/server/rpc/EnumMapUtil.java b/user/src/com/google/gwt/user/server/rpc/EnumMapUtil.java new file mode 100644 index 00000000000..b8fd7d750fe --- /dev/null +++ b/user/src/com/google/gwt/user/server/rpc/EnumMapUtil.java @@ -0,0 +1,100 @@ +/* + * Copyright 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.user.server.rpc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; + +/** + * A utility to extract the key type of any {@link java.util.EnumMap}, even if it is empty. Some + * frameworks have tried this by using reflection on the map's private field {@code keyType}, but + * that approach breaks with Java 17 where reflection on private elements of any {@code java.*} + * class is forbidden unless the {@code --add-opens} VM argument is used which is a fairly unsafe + * thing to do. + *

+ * + * Use like this:

+ *      private static enum MyBoolean {
+ *          TRUE, FALSE
+ *      };
+ *      EnumMap enumMap = new EnumMap<>(MyBoolean.class);
+ *      Class c = EnumMapUtil.getKeyType(enumMap);
+ * 
+ *

+ * + * Implementation note: If the map passed to {@link #getKeyType(java.util.EnumMap)} is not empty, + * the first key is obtained from the {@link EnumMap#keySet() key set} and its type is determined + * and returned. If the map is empty, it is serialized into an {@link ObjectOutputStream} writing to + * a {@link ByteArrayOutputStream} from which it is read again with a specialized + * {@link ObjectInputStream}where the {@code resolveClass} method is overridden. Upon reading a + * class descriptor for class {@link java.util.EnumMap}, instead of regularly resolving the class + * descriptor a package-protected class {@link com.google.gwt.user.server.rpc.EnumMap} is returned + * which has a serial version UID equal to that of {@link java.util.EnumMap} and the same set of + * fields. By having the same simple name (despite living in a different package) the + * {@link ObjectInputStream} considers this class to be compatible to {@link java.util.EnumMap} and + * de-serializes the stream contents into an instance of + * {@link com.google.gwt.user.server.rpc.EnumMap} which in turn offers a public getter + * {@link com.google.gwt.user.server.rpc.EnumMap#getKeyType()} for the key type. Through this getter + * the key type is then determined and returned. + * + */ +public class EnumMapUtil { + private static class MyObjectInputStream extends ObjectInputStream { + public MyObjectInputStream(InputStream in) throws IOException { + super(in); + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, + ClassNotFoundException { + Class result = null; + if (desc.getName().equals("java.util.EnumMap")) { + result = EnumMap.class; + } else { + try { + result = super.resolveClass(desc); + } catch (ClassNotFoundException e) { + result = Class.forName(desc.getName(), false, Thread.currentThread() + .getContextClassLoader()); + } + } + return result; + } + } + + public static , V> Class getKeyType(java.util.EnumMap enumMap) + throws IOException, ClassNotFoundException { + final Class result; + if (enumMap.isEmpty()) { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(enumMap); + oos.close(); + final ObjectInputStream ois = new MyObjectInputStream(new ByteArrayInputStream(bos + .toByteArray())); + @SuppressWarnings("unchecked") + final EnumMap readMap = (EnumMap) ois.readObject(); + final Class keyType = readMap.getKeyType(); + result = keyType; + } else { + result = enumMap.keySet().iterator().next().getDeclaringClass(); + } + return result; + } +} diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java index 54fbbaa79c5..8a2a50fb012 100644 --- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java +++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPOutputStream; @@ -35,15 +36,16 @@ * the RPC system. */ public class RPCServletUtils { - + public static final String CHARSET_UTF8_NAME = "UTF-8"; - + /** - * The UTF-8 Charset. Use this to avoid concurrency bottlenecks when - * converting between byte arrays and Strings. - * See http://code.google.com/p/google-web-toolkit/issues/detail?id=6398 + * The UTF-8 Charset. + * + * Deprecated: please use {@link StandardCharsets#UTF_8} instead. */ - public static final Charset CHARSET_UTF8 = Charset.forName(CHARSET_UTF8_NAME); + @Deprecated + public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; /** * Package protected for use in tests. @@ -73,17 +75,16 @@ public class RPCServletUtils { private static final int UNCOMPRESSED_BYTE_SIZE_LIMIT = 256; /** - * Contains cached mappings from character set name to Charset. The - * null key maps to the default UTF-8 character set. + * Contains cached mappings from character set name to Charset. */ private static final ConcurrentHashMap CHARSET_CACHE = new ConcurrentHashMap(); - /** - * Pre-populate the character set cache with UTF-8. + /* + Pre-populate the character set cache with UTF-8. */ static { - CHARSET_CACHE.put(CHARSET_UTF8_NAME, CHARSET_UTF8); + CHARSET_CACHE.put(CHARSET_UTF8_NAME, StandardCharsets.UTF_8); } /** @@ -131,7 +132,7 @@ public static boolean exceedsUncompressedContentLengthLimit(String content) { public static Charset getCharset(String encoding) { if (encoding == null) { - return CHARSET_UTF8; + return StandardCharsets.UTF_8; } Charset charset = CHARSET_CACHE.get(encoding); @@ -335,7 +336,7 @@ public static void writeResponse(ServletContext servletContext, HttpServletResponse response, String responseContent, boolean gzipResponse) throws IOException { - byte[] responseBytes = responseContent.getBytes(CHARSET_UTF8); + byte[] responseBytes = responseContent.getBytes(StandardCharsets.UTF_8); if (gzipResponse) { // Compress the reply and adjust headers. // @@ -396,7 +397,7 @@ public static void writeResponseForUnexpectedFailure( response.setContentType("text/plain"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); try { - response.getOutputStream().write(GENERIC_FAILURE_MSG.getBytes(CHARSET_UTF8)); + response.getOutputStream().write(GENERIC_FAILURE_MSG.getBytes(StandardCharsets.UTF_8)); } catch (IllegalStateException e) { // Handle the (unexpected) case where getWriter() was previously used response.getWriter().write(GENERIC_FAILURE_MSG); diff --git a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java index b7b1a64a4b7..febcb1a863e 100644 --- a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java +++ b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java @@ -21,7 +21,6 @@ import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; -import com.google.gwt.user.server.rpc.RPCServletUtils; import com.google.gwt.user.server.rpc.SerializationPolicy; import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer; @@ -34,6 +33,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -896,7 +896,7 @@ private static void findInstanceParameters(Class instanceClass, private static void generateSerializationSignature(Class instanceType, CRC32 crc, SerializationPolicy policy) throws UnsupportedEncodingException { - crc.update(getSerializedTypeName(instanceType).getBytes(RPCServletUtils.CHARSET_UTF8)); + crc.update(getSerializedTypeName(instanceType).getBytes(StandardCharsets.UTF_8)); if (excludeImplementationFromSerializationSignature(instanceType)) { return; @@ -913,7 +913,7 @@ private static void generateSerializationSignature(Class instanceType, CRC32 } Enum[] constants = instanceType.asSubclass(Enum.class).getEnumConstants(); for (Enum constant : constants) { - crc.update(constant.name().getBytes(RPCServletUtils.CHARSET_UTF8)); + crc.update(constant.name().getBytes(StandardCharsets.UTF_8)); } } else if (!instanceType.isPrimitive()) { Field[] fields = applyFieldSerializationPolicy(instanceType, policy); @@ -925,8 +925,8 @@ private static void generateSerializationSignature(Class instanceType, CRC32 * generate the signature. Otherwise, use all known fields. */ if ((clientFieldNames == null) || clientFieldNames.contains(field.getName())) { - crc.update(field.getName().getBytes(RPCServletUtils.CHARSET_UTF8)); - crc.update(getSerializedTypeName(field.getType()).getBytes(RPCServletUtils.CHARSET_UTF8)); + crc.update(field.getName().getBytes(StandardCharsets.UTF_8)); + crc.update(getSerializedTypeName(field.getType()).getBytes(StandardCharsets.UTF_8)); } } diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java b/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java index b6964b2731e..c52face025f 100644 --- a/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java +++ b/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java @@ -207,6 +207,9 @@ public Type[] getActualTypeArguments() { Object genericDeclaration = variable.getGenericDeclaration(); if (genericDeclaration instanceof Class) { Class declaration = (Class) genericDeclaration; + if (declaration.equals(containingType)) { + return ensureBaseType(type); + } // could probably optimize, but would involve duplicating a bunch of // getParameterization's code Type[] types = getParameterization(declaration, containingType); diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java index 4e12d769b9d..1b09125086c 100644 --- a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java +++ b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java @@ -352,7 +352,7 @@ private class AnnotationProcessor extends AnnotationVisitor { public AnnotationProcessor(String sourceType, AnnotationVisitor av) { // TODO(rluble): should we chain av to super here? - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.sourceType = sourceType; this.av = av; } @@ -386,7 +386,7 @@ private class ClassProcessor extends ClassVisitor { private String sourceType; public ClassProcessor(String sourceType, ClassVisitor cv, State state) { - super(Opcodes.ASM7, cv); + super(Opcodes.ASM9, cv); this.sourceType = sourceType; this.state = state; } @@ -519,7 +519,7 @@ private class FieldProcessor extends FieldVisitor { public FieldProcessor(String sourceType, FieldVisitor fv) { // TODO(rluble): Should we chain fv to super here? - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.sourceType = sourceType; this.fv = fv; } @@ -541,7 +541,7 @@ private class MethodProcessor extends MethodVisitor { private final String sourceType; public MethodProcessor(String sourceType, MethodVisitor mv) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); this.sourceType = sourceType; } @@ -630,7 +630,7 @@ public void visitTypeInsn(int opcode, String type) { */ private class NativeMethodDefanger extends ClassVisitor { public NativeMethodDefanger(ClassVisitor cv) { - super(Opcodes.ASM7, cv); + super(Opcodes.ASM9, cv); } @Override diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java index 693efeba2c2..8950a41236f 100644 --- a/user/super/com/google/gwt/emul/java/lang/Long.java +++ b/user/super/com/google/gwt/emul/java/lang/Long.java @@ -237,7 +237,7 @@ private static String toPowerOfTwoUnsignedString(long value, int shift) { int highBits = LongUtils.getHighBits(value); if (highBits == 0) { - return Integer.toString((int) value, radix); + return Integer.toUnsignedString((int) value, radix); } final int mask = radix - 1; diff --git a/user/super/com/google/gwt/emul/java/util/Enumeration.java b/user/super/com/google/gwt/emul/java/util/Enumeration.java index 1f0c8adce1c..26fc44b0ee2 100644 --- a/user/super/com/google/gwt/emul/java/util/Enumeration.java +++ b/user/super/com/google/gwt/emul/java/util/Enumeration.java @@ -27,4 +27,17 @@ public interface Enumeration { boolean hasMoreElements(); E nextElement(); + + default Iterator asIterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return hasMoreElements(); + } + @Override + public E next() { + return nextElement(); + } + }; + } } diff --git a/user/super/com/google/gwt/emul/java/util/List.java b/user/super/com/google/gwt/emul/java/util/List.java index f8c3ac7f028..d1d20110b82 100644 --- a/user/super/com/google/gwt/emul/java/util/List.java +++ b/user/super/com/google/gwt/emul/java/util/List.java @@ -18,6 +18,7 @@ import static javaemul.internal.InternalPreconditions.checkNotNull; import java.util.function.UnaryOperator; +import javaemul.internal.ArrayHelper; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsMethod; @@ -34,6 +35,85 @@ @JsType public interface List extends Collection { + @JsIgnore + static List of() { + return Collections.unmodifiableList(Collections.emptyList()); + } + + @JsIgnore + static List of(E e1) { + return __ofInternal((E[]) new Object[] {e1}); + } + + @JsIgnore + static List of(E e1, E e2) { + return __ofInternal((E[]) new Object[] {e1, e2}); + } + + @JsIgnore + static List of(E e1, E e2, E e3) { + return __ofInternal((E[]) new Object[] {e1, e2, e3}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5, E e6) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5, e6}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8, e9}); + } + + @JsIgnore + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return __ofInternal((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8, e9, e10}); + } + + // CHECKSTYLE_OFF: Internal only method that cannot collide with future JRE changes + /** + * Internal-only helper to avoid copying the incoming array, and instead just wrap it with an + * immutable List after checking there are no nulls. + */ + @JsIgnore + static List __ofInternal(E[] elements) { + for (int i = 0; i < elements.length; i++) { + checkNotNull(elements[i]); + } + return Collections.unmodifiableList(Arrays.asList(elements)); + } + // CHECKSTYLE_ON + + @JsIgnore + static List of(E... elements) { + for (int i = 0; i < elements.length; i++) { + checkNotNull(elements[i]); + } + return Collections.unmodifiableList( + Arrays.asList((E[]) ArrayHelper.unsafeClone(elements, 0, elements.length)) + ); + } + @JsMethod(name = "addAtIndex") void add(int index, E element); diff --git a/user/super/com/google/gwt/emul/java/util/Map.java b/user/super/com/google/gwt/emul/java/util/Map.java index 9c134bc6b30..fd3178b0411 100644 --- a/user/super/com/google/gwt/emul/java/util/Map.java +++ b/user/super/com/google/gwt/emul/java/util/Map.java @@ -15,6 +15,7 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkArgument; import static javaemul.internal.InternalPreconditions.checkNotNull; import java.io.Serializable; @@ -35,6 +36,211 @@ @JsType public interface Map { + @JsIgnore + static Map of() { + return Collections.unmodifiableMap(Collections.emptyMap()); + } + + @JsIgnore + static Map of(K key, V value) { + return ofEntries( + entry(key, value) + ); + } + + @JsIgnore + static Map of(K k1, V v1, K k2, V v2) { + return ofEntries( + entry(k1, v1), + entry(k2, v2) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5, + K k6, V v6 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5), + entry(k6, v6) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5, + K k6, V v6, + K k7, V v7 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5), + entry(k6, v6), + entry(k7, v7) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5, + K k6, V v6, + K k7, V v7, + K k8, V v8 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5), + entry(k6, v6), + entry(k7, v7), + entry(k8, v8) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5, + K k6, V v6, + K k7, V v7, + K k8, V v8, + K k9, V v9 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5), + entry(k6, v6), + entry(k7, v7), + entry(k8, v8), + entry(k9, v9) + ); + } + + @JsIgnore + static Map of( + K k1, V v1, + K k2, V v2, + K k3, V v3, + K k4, V v4, + K k5, V v5, + K k6, V v6, + K k7, V v7, + K k8, V v8, + K k9, V v9, + K k10, V v10 + ) { + return ofEntries( + entry(k1, v1), + entry(k2, v2), + entry(k3, v3), + entry(k4, v4), + entry(k5, v5), + entry(k6, v6), + entry(k7, v7), + entry(k8, v8), + entry(k9, v9), + entry(k10, v10) + ); + } + + @JsIgnore + static Entry entry(K key, V value) { + // This isn't quite consistent with the javadoc, since this is serializable, while entry() + // need not be serializable. + return new AbstractMap.SimpleImmutableEntry<>( + checkNotNull(key), + checkNotNull(value) + ); + } + + @JsIgnore + static Map ofEntries(Entry... entries) { + Map map = new HashMap<>(); + + for (int i = 0; i < entries.length; i++) { + // TODO this perhaps can be optimized if we know the entry is an instance of + // AbstractMap.SimpleImmutableEntry, or something more specialized? + Entry entry = checkNotNull(entries[i]); + checkArgument(map.put(checkNotNull(entry.getKey()), checkNotNull(entry.getValue())) == null, + "Can't add multiple entries with the same key"); + } + + return Collections.unmodifiableMap(map); + } + /** * Represents an individual map entry. */ diff --git a/user/super/com/google/gwt/emul/java/util/Optional.java b/user/super/com/google/gwt/emul/java/util/Optional.java index 32584444093..9485ce88fc1 100644 --- a/user/super/com/google/gwt/emul/java/util/Optional.java +++ b/user/super/com/google/gwt/emul/java/util/Optional.java @@ -15,14 +15,15 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkCriticalElement; +import static javaemul.internal.InternalPreconditions.checkCriticalNotNull; +import static javaemul.internal.InternalPreconditions.checkNotNull; + import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; - -import static javaemul.internal.InternalPreconditions.checkCriticalElement; -import static javaemul.internal.InternalPreconditions.checkCriticalNotNull; -import static javaemul.internal.InternalPreconditions.checkNotNull; +import java.util.stream.Stream; /** * See @@ -72,6 +73,14 @@ public void ifPresent(Consumer consumer) { } } + public void ifPresentOrElse(Consumer action, Runnable emptyAction) { + if (isPresent()) { + action.accept(ref); + } else { + emptyAction.run(); + } + } + public Optional filter(Predicate predicate) { checkNotNull(predicate); if (!isPresent() || predicate.test(ref)) { @@ -96,6 +105,23 @@ public Optional flatMap(Function> mapper) { return empty(); } + public Optional or(Supplier> supplier) { + checkNotNull(supplier); + if (isPresent()) { + return this; + } else { + return (Optional) checkNotNull(supplier.get()); + } + } + + public Stream stream() { + if (isPresent()) { + return Stream.of(ref); + } else { + return Stream.empty(); + } + } + public T orElse(T other) { return isPresent() ? ref : other; } @@ -104,6 +130,10 @@ public T orElseGet(Supplier other) { return isPresent() ? ref : other.get(); } + public T orElseThrow() { + return get(); + } + public T orElseThrow(Supplier exceptionSupplier) throws X { if (isPresent()) { return ref; diff --git a/user/super/com/google/gwt/emul/java/util/OptionalDouble.java b/user/super/com/google/gwt/emul/java/util/OptionalDouble.java index e6b06af100a..b7a5151e832 100644 --- a/user/super/com/google/gwt/emul/java/util/OptionalDouble.java +++ b/user/super/com/google/gwt/emul/java/util/OptionalDouble.java @@ -15,11 +15,12 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkCriticalElement; + import java.util.function.DoubleConsumer; import java.util.function.DoubleSupplier; import java.util.function.Supplier; - -import static javaemul.internal.InternalPreconditions.checkCriticalElement; +import java.util.stream.DoubleStream; /** * See @@ -54,6 +55,10 @@ public boolean isPresent() { return present; } + public boolean isEmpty() { + return !present; + } + public double getAsDouble() { checkCriticalElement(present); return ref; @@ -65,6 +70,22 @@ public void ifPresent(DoubleConsumer consumer) { } } + public void ifPresentOrElse(DoubleConsumer action, Runnable emptyAction) { + if (isPresent()) { + action.accept(ref); + } else { + emptyAction.run(); + } + } + + public DoubleStream stream() { + if (isPresent()) { + return DoubleStream.of(ref); + } else { + return DoubleStream.empty(); + } + } + public double orElse(double other) { return present ? ref : other; } @@ -73,6 +94,10 @@ public double orElseGet(DoubleSupplier other) { return present ? ref : other.getAsDouble(); } + public double orElseThrow() { + return getAsDouble(); + } + public double orElseThrow(Supplier exceptionSupplier) throws X { if (present) { return ref; diff --git a/user/super/com/google/gwt/emul/java/util/OptionalInt.java b/user/super/com/google/gwt/emul/java/util/OptionalInt.java index 10b7ce66394..cd4ab8fb3ee 100644 --- a/user/super/com/google/gwt/emul/java/util/OptionalInt.java +++ b/user/super/com/google/gwt/emul/java/util/OptionalInt.java @@ -15,11 +15,12 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkCriticalElement; + import java.util.function.IntConsumer; import java.util.function.IntSupplier; import java.util.function.Supplier; - -import static javaemul.internal.InternalPreconditions.checkCriticalElement; +import java.util.stream.IntStream; /** * See @@ -54,6 +55,10 @@ public boolean isPresent() { return present; } + public boolean isEmpty() { + return !present; + } + public int getAsInt() { checkCriticalElement(present); return ref; @@ -65,6 +70,22 @@ public void ifPresent(IntConsumer consumer) { } } + public void ifPresentOrElse(IntConsumer action, Runnable emptyAction) { + if (present) { + action.accept(ref); + } else { + emptyAction.run(); + } + } + + public IntStream stream() { + if (present) { + return IntStream.of(ref); + } else { + return IntStream.empty(); + } + } + public int orElse(int other) { return present ? ref : other; } @@ -73,6 +94,10 @@ public int orElseGet(IntSupplier other) { return present ? ref : other.getAsInt(); } + public int orElseThrow() { + return getAsInt(); + } + public int orElseThrow(Supplier exceptionSupplier) throws X { if (present) { return ref; diff --git a/user/super/com/google/gwt/emul/java/util/OptionalLong.java b/user/super/com/google/gwt/emul/java/util/OptionalLong.java index 8551777860b..11394bb4c70 100644 --- a/user/super/com/google/gwt/emul/java/util/OptionalLong.java +++ b/user/super/com/google/gwt/emul/java/util/OptionalLong.java @@ -15,11 +15,12 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkCriticalElement; + import java.util.function.LongConsumer; import java.util.function.LongSupplier; import java.util.function.Supplier; - -import static javaemul.internal.InternalPreconditions.checkCriticalElement; +import java.util.stream.LongStream; /** * See @@ -54,6 +55,10 @@ public boolean isPresent() { return present; } + public boolean isEmpty() { + return !present; + } + public long getAsLong() { checkCriticalElement(present); return ref; @@ -65,6 +70,22 @@ public void ifPresent(LongConsumer consumer) { } } + public void ifPresentOrElse(LongConsumer action, Runnable emptyAction) { + if (present) { + action.accept(ref); + } else { + emptyAction.run(); + } + } + + public LongStream stream() { + if (present) { + return LongStream.of(ref); + } else { + return LongStream.empty(); + } + } + public long orElse(long other) { return present ? ref : other; } @@ -73,6 +94,10 @@ public long orElseGet(LongSupplier other) { return present ? ref : other.getAsLong(); } + public long orElseThrow() { + return getAsLong(); + } + public long orElseThrow(Supplier exceptionSupplier) throws X { if (present) { return ref; diff --git a/user/super/com/google/gwt/emul/java/util/Set.java b/user/super/com/google/gwt/emul/java/util/Set.java index 8a5bf8cb387..eddd4dffe90 100644 --- a/user/super/com/google/gwt/emul/java/util/Set.java +++ b/user/super/com/google/gwt/emul/java/util/Set.java @@ -15,6 +15,9 @@ */ package java.util; +import static javaemul.internal.InternalPreconditions.checkArgument; +import static javaemul.internal.InternalPreconditions.checkNotNull; + import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsType; @@ -26,6 +29,70 @@ */ @JsType public interface Set extends Collection { + @JsIgnore + static Set of() { + return Collections.unmodifiableSet(Collections.emptySet()); + } + + @JsIgnore + static Set of(E e1) { + return of((E[]) new Object[] {e1}); + } + + @JsIgnore + static Set of(E e1, E e2) { + return of((E[]) new Object[] {e1, e2}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3) { + return of((E[]) new Object[] {e1, e2, e3}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4) { + return of((E[]) new Object[] {e1, e2, e3, e4}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5, E e6) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5, e6}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8, e9}); + } + + @JsIgnore + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return of((E[]) new Object[] {e1, e2, e3, e4, e5, e6, e7, e8, e9, e10}); + } + + @JsIgnore + static Set of(E... elements) { + HashSet set = new HashSet<>(); + for (int i = 0; i < elements.length; i++) { + checkArgument(set.add(checkNotNull(elements[i])), "Can't add the same item multiple times"); + } + return Collections.unmodifiableSet(set); + } + @JsIgnore @Override default Spliterator spliterator() { diff --git a/user/super/com/google/gwt/emul/java/util/stream/Collectors.java b/user/super/com/google/gwt/emul/java/util/stream/Collectors.java index d19f64cae2b..60d908edb95 100644 --- a/user/super/com/google/gwt/emul/java/util/stream/Collectors.java +++ b/user/super/com/google/gwt/emul/java/util/stream/Collectors.java @@ -170,6 +170,38 @@ public static Collector joining(CharSequence delimiter) { downstream.finisher()); } + public static Collector flatMapping(Function> mapper, Collector downstream) { + return new CollectorImpl<>( + downstream.supplier(), + (A a, T t) -> { + Stream stream = mapper.apply(t); + if (stream == null) { + return; + } + stream.forEach(u -> { + downstream.accumulator().accept(a, u); + }); + }, + downstream.combiner(), + downstream.finisher() + ); + } + + public static Collector filtering(Predicate predicate, + Collector downstream) { + return new CollectorImpl<>( + downstream.supplier(), + (a, t) -> { + if (predicate.test(t)) { + downstream.accumulator().accept(a, t); + } + }, + downstream.combiner(), + downstream.finisher() + ); + } + public static Collector> maxBy(Comparator comparator) { return reducing(BinaryOperator.maxBy(comparator)); } diff --git a/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java index 012bd0982f7..c0d81589a49 100644 --- a/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java +++ b/user/super/com/google/gwt/emul/java/util/stream/DoubleStream.java @@ -146,19 +146,35 @@ public boolean tryAdvance(DoubleConsumer action) { } static DoubleStream iterate(double seed, DoubleUnaryOperator f) { + return iterate(seed, ignore -> true, f); + } + + static DoubleStream iterate(double seed, DoublePredicate hasNext, DoubleUnaryOperator f) { Spliterator.OfDouble spliterator = new Spliterators.AbstractDoubleSpliterator( Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) { + private boolean first = true; private double next = seed; + private boolean terminated = false; @Override public boolean tryAdvance(DoubleConsumer action) { + if (terminated) { + return false; + } + if (!first) { + next = f.applyAsDouble(next); + } + first = false; + + if (!hasNext.test(next)) { + terminated = true; + return false; + } action.accept(next); - next = f.applyAsDouble(next); return true; } }; - return StreamSupport.doubleStream(spliterator, false); } @@ -185,6 +201,39 @@ static DoubleStream of(double t) { DoubleStream distinct(); + default DoubleStream dropWhile(DoublePredicate predicate) { + Spliterator.OfDouble prev = spliterator(); + Spliterator.OfDouble spliterator = + new Spliterators.AbstractDoubleSpliterator(prev.estimateSize(), + prev.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean drop = true; + private boolean found; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + found = false; + if (drop) { + // drop items until we find one that matches + while (drop && prev.tryAdvance((double item) -> { + if (!predicate.test(item)) { + drop = false; + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + // only return true if we accepted at least one item + return found; + } else { + // accept one item, return result + return prev.tryAdvance(action); + } + } + }; + return StreamSupport.doubleStream(spliterator, false); + } + DoubleStream filter(DoublePredicate predicate); OptionalDouble findAny(); @@ -239,5 +288,34 @@ static DoubleStream of(double t) { DoubleSummaryStatistics summaryStatistics(); + default DoubleStream takeWhile(DoublePredicate predicate) { + Spliterator.OfDouble original = spliterator(); + Spliterator.OfDouble spliterator = + new Spliterators.AbstractDoubleSpliterator(original.estimateSize(), + original.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean take = true; + private boolean found; + + @Override + public boolean tryAdvance(DoubleConsumer action) { + found = false; + if (!take) { + // already failed the check + return false; + } + original.tryAdvance((double item) -> { + if (predicate.test(item)) { + found = true; + action.accept(item); + } else { + take = false; + } + }); + return found; + } + }; + return StreamSupport.doubleStream(spliterator, false); + } + double[] toArray(); } diff --git a/user/super/com/google/gwt/emul/java/util/stream/IntStream.java b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java index e661b5ca997..bac1b49844d 100644 --- a/user/super/com/google/gwt/emul/java/util/stream/IntStream.java +++ b/user/super/com/google/gwt/emul/java/util/stream/IntStream.java @@ -149,20 +149,35 @@ public boolean tryAdvance(IntConsumer action) { } static IntStream iterate(int seed, IntUnaryOperator f) { + return iterate(seed, ignore -> true, f); + } - AbstractIntSpliterator spliterator = + static IntStream iterate(int seed, IntPredicate hasNext, IntUnaryOperator f) { + Spliterator.OfInt spliterator = new Spliterators.AbstractIntSpliterator( Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) { + private boolean first = true; private int next = seed; + private boolean terminated = false; @Override public boolean tryAdvance(IntConsumer action) { + if (terminated) { + return false; + } + if (!first) { + next = f.applyAsInt(next); + } + first = false; + + if (!hasNext.test(next)) { + terminated = true; + return false; + } action.accept(next); - next = f.applyAsInt(next); return true; } }; - return StreamSupport.intStream(spliterator, false); } @@ -235,6 +250,39 @@ public boolean tryAdvance(IntConsumer action) { IntStream distinct(); + default IntStream dropWhile(IntPredicate predicate) { + Spliterator.OfInt prev = spliterator(); + Spliterator.OfInt spliterator = + new Spliterators.AbstractIntSpliterator(prev.estimateSize(), + prev.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean drop = true; + private boolean found; + + @Override + public boolean tryAdvance(IntConsumer action) { + found = false; + if (drop) { + // drop items until we find one that matches + while (drop && prev.tryAdvance((int item) -> { + if (!predicate.test(item)) { + drop = false; + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + // only return true if we accepted at least one item + return found; + } else { + // accept one item, return result + return prev.tryAdvance(action); + } + } + }; + return StreamSupport.intStream(spliterator, false); + } + IntStream filter(IntPredicate predicate); OptionalInt findAny(); @@ -289,5 +337,34 @@ public boolean tryAdvance(IntConsumer action) { IntSummaryStatistics summaryStatistics(); + default IntStream takeWhile(IntPredicate predicate) { + Spliterator.OfInt original = spliterator(); + Spliterator.OfInt spliterator = + new Spliterators.AbstractIntSpliterator(original.estimateSize(), + original.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean take = true; + private boolean found; + + @Override + public boolean tryAdvance(IntConsumer action) { + found = false; + if (!take) { + // already failed the check + return false; + } + original.tryAdvance((int item) -> { + if (predicate.test(item)) { + found = true; + action.accept(item); + } else { + take = false; + } + }); + return found; + } + }; + return StreamSupport.intStream(spliterator, false); + } + int[] toArray(); } diff --git a/user/super/com/google/gwt/emul/java/util/stream/LongStream.java b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java index 63861467c92..821c2f7f40e 100644 --- a/user/super/com/google/gwt/emul/java/util/stream/LongStream.java +++ b/user/super/com/google/gwt/emul/java/util/stream/LongStream.java @@ -149,15 +149,32 @@ public boolean tryAdvance(LongConsumer action) { } static LongStream iterate(long seed, LongUnaryOperator f) { - AbstractLongSpliterator spliterator = + return iterate(seed, ignore -> true, f); + } + + static LongStream iterate(long seed, LongPredicate hasNext, LongUnaryOperator f) { + Spliterator.OfLong spliterator = new Spliterators.AbstractLongSpliterator( Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) { + private boolean first = true; private long next = seed; + private boolean terminated = false; @Override public boolean tryAdvance(LongConsumer action) { + if (terminated) { + return false; + } + if (!first) { + next = f.applyAsLong(next); + } + first = false; + + if (!hasNext.test(next)) { + terminated = true; + return false; + } action.accept(next); - next = f.applyAsLong(next); return true; } }; @@ -231,6 +248,39 @@ public boolean tryAdvance(LongConsumer action) { LongStream distinct(); + default LongStream dropWhile(LongPredicate predicate) { + Spliterator.OfLong prev = spliterator(); + Spliterator.OfLong spliterator = + new Spliterators.AbstractLongSpliterator(prev.estimateSize(), + prev.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean drop = true; + private boolean found; + + @Override + public boolean tryAdvance(LongConsumer action) { + found = false; + if (drop) { + // drop items until we find one that matches + while (drop && prev.tryAdvance((long item) -> { + if (!predicate.test(item)) { + drop = false; + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + // only return true if we accepted at least one item + return found; + } else { + // accept one item, return result + return prev.tryAdvance(action); + } + } + }; + return StreamSupport.longStream(spliterator, false); + } + LongStream filter(LongPredicate predicate); OptionalLong findAny(); @@ -285,5 +335,34 @@ public boolean tryAdvance(LongConsumer action) { LongSummaryStatistics summaryStatistics(); + default LongStream takeWhile(LongPredicate predicate) { + Spliterator.OfLong original = spliterator(); + Spliterator.OfLong spliterator = + new Spliterators.AbstractLongSpliterator(original.estimateSize(), + original.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean take = true; + private boolean found; + + @Override + public boolean tryAdvance(LongConsumer action) { + found = false; + if (!take) { + // already failed the check + return false; + } + original.tryAdvance((long item) -> { + if (predicate.test(item)) { + found = true; + action.accept(item); + } else { + take = false; + } + }); + return found; + } + }; + return StreamSupport.longStream(spliterator, false); + } + long[] toArray(); } diff --git a/user/super/com/google/gwt/emul/java/util/stream/Stream.java b/user/super/com/google/gwt/emul/java/util/stream/Stream.java index 70a9dd9c827..386d3293915 100644 --- a/user/super/com/google/gwt/emul/java/util/stream/Stream.java +++ b/user/super/com/google/gwt/emul/java/util/stream/Stream.java @@ -150,15 +150,32 @@ public boolean tryAdvance(Consumer action) { } static Stream iterate(T seed, UnaryOperator f) { + return iterate(seed, ignore -> true, f); + } + + static Stream iterate(T seed, Predicate hasNext, UnaryOperator f) { AbstractSpliterator spliterator = new Spliterators.AbstractSpliterator( Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.ORDERED) { + private boolean first = true; private T next = seed; + private boolean terminated = false; @Override public boolean tryAdvance(Consumer action) { + if (terminated) { + return false; + } + if (!first) { + next = f.apply(next); + } + first = false; + + if (!hasNext.test(next)) { + terminated = true; + return false; + } action.accept(next); - next = f.apply(next); return true; } }; @@ -175,6 +192,14 @@ static Stream of(T... values) { return Arrays.stream(values); } + static Stream ofNullable(T t) { + if (t == null) { + return empty(); + } else { + return of(t); + } + } + boolean allMatch(Predicate predicate); boolean anyMatch(Predicate predicate); @@ -188,6 +213,39 @@ R collect( Stream distinct(); + default Stream dropWhile(Predicate predicate) { + Spliterator prev = spliterator(); + Spliterator spliterator = + new Spliterators.AbstractSpliterator(prev.estimateSize(), + prev.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean drop = true; + private boolean found; + + @Override + public boolean tryAdvance(Consumer action) { + found = false; + if (drop) { + // drop items until we find one that matches + while (drop && prev.tryAdvance(item -> { + if (!predicate.test(item)) { + drop = false; + found = true; + action.accept(item); + } + })) { + // do nothing, work is done in tryAdvance + } + // only return true if we accepted at least one item + return found; + } else { + // accept one item, return result + return prev.tryAdvance(action); + } + } + }; + return StreamSupport.stream(spliterator, false); + } + Stream filter(Predicate predicate); Optional findAny(); @@ -236,6 +294,35 @@ R collect( Stream sorted(Comparator comparator); + default Stream takeWhile(Predicate predicate) { + Spliterator original = spliterator(); + Spliterator spliterator = + new Spliterators.AbstractSpliterator(original.estimateSize(), + original.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED)) { + private boolean take = true; + private boolean found; + + @Override + public boolean tryAdvance(Consumer action) { + found = false; + if (!take) { + // already failed the check + return false; + } + original.tryAdvance(item -> { + if (predicate.test(item)) { + found = true; + action.accept(item); + } else { + take = false; + } + }); + return found; + } + }; + return StreamSupport.stream(spliterator, false); + } + Object[] toArray(); A[] toArray(IntFunction generator); diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java index a251c731ccb..9f3c4e02899 100644 --- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java +++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java @@ -2003,78 +2003,6 @@ public void testLambdaBoxing() { assertEquals(2, (int) unboxBox.apply(new Integer(2))); } - //////////////////////////////////////////////////////////// - // - // Tests for language features introduced in Java 9 - - class Resource implements AutoCloseable { - boolean isOpen = true; - - public void close() { - this.isOpen = false; - } - } - - public void testTryWithResourcesJava9() { - Resource r1 = new Resource(); - assertTrue(r1.isOpen); - Resource r2Copy; - try (r1; Resource r2 = new Resource()) { - assertTrue(r1.isOpen); - assertTrue(r2.isOpen); - r2Copy = r2; - } - assertFalse(r1.isOpen); - assertFalse(r2Copy.isOpen); - } - - private interface InterfaceWithPrivateMethods { - int implementedMethod(); - - default int defaultMethod() { - return privateMethod(); - } - - private int privateMethod() { - return implementedMethod(); - } - - private int staticPrivateMethod() { - return 42; - } - } - - public void testInterfacePrivateMethodsJava9() { - InterfaceWithPrivateMethods implementor = () -> 50; - assertEquals(50, implementor.implementedMethod()); - assertEquals(50, implementor.defaultMethod()); - assertEquals(42, implementor.staticPrivateMethod()); - } - - public void testAnonymousDiamondJava9() { - Supplier helloSupplier = new Supplier<>() { - @Override - public String get() { - return "hello"; - } - }; - assertEquals("hello", helloSupplier.get()); - } - - interface Selector extends Predicate { - @Override - boolean test(String object); - - default Selector trueSelector() { - // Unused variable that creates a lambda with a bridge for the method test. The bug #9598 - // was caused by GwtAstBuilder associating the bridge method Lambda.test(Object) on the - // lambda below to the method Predicate.test(Object), causing the method resolution in the - // code that refers to the Predicate.test(Object) in the test below to refer to - // Lambda.test(Object) which is the wrong method. - return receiver -> true; - } - } - // Regression tests for #9598 public void testImproperMethodResolution() { Predicate p = o -> true; @@ -2099,11 +2027,11 @@ public void testIntersectionCastLambda() { assertEquals("#2", lambda.foo("2")); } - static class C { public static String append(String str) { return "#" + str; } } + static class C2 { public static String append(String str) { return "#" + str; } } @SuppressWarnings({"rawtypes", "unchecked"}) public void testIntersectionCastMethodReference() { - Object instance = (I1 & I2) C::append; + Object instance = (I1 & I2) C2::append; assertTrue(instance instanceof I1); assertTrue(instance instanceof I2); diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java9Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java9Test.java new file mode 100644 index 00000000000..e6ff6d29d3f --- /dev/null +++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java9Test.java @@ -0,0 +1,99 @@ +/* + * Copyright 2023 GwtProject contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.jjs.test; + +import com.google.gwt.core.client.GwtScriptOnly; +import com.google.gwt.junit.client.GWTTestCase; + +import java.util.function.Predicate; +import java.util.function.Supplier; + +@GwtScriptOnly +public class Java9Test extends GWTTestCase { + + @Override + public String getModuleName() { + return "com.google.gwt.dev.jjs.Java9Test"; + } + + class Resource implements AutoCloseable { + boolean isOpen = true; + + public void close() { + this.isOpen = false; + } + } + + public void testTryWithResourcesJava9() { + Resource r1 = new Resource(); + assertTrue(r1.isOpen); + Resource r2Copy; + try (r1; Resource r2 = new Resource()) { + assertTrue(r1.isOpen); + assertTrue(r2.isOpen); + r2Copy = r2; + } + assertFalse(r1.isOpen); + assertFalse(r2Copy.isOpen); + } + + interface Selector extends Predicate { + @Override + boolean test(String object); + + default Selector trueSelector() { + // Unused variable that creates a lambda with a bridge for the method test. The bug #9598 + // was caused by GwtAstBuilder associating the bridge method Lambda.test(Object) on the + // lambda below to the method Predicate.test(Object), causing the method resolution in the + // code that refers to the Predicate.test(Object) in the test below to refer to + // Lambda.test(Object) which is the wrong method. + return receiver -> true; + } + } + + private interface InterfaceWithPrivateMethods { + int implementedMethod(); + + default int defaultMethod() { + return privateMethod(); + } + + private int privateMethod() { + return implementedMethod(); + } + + private int staticPrivateMethod() { + return 42; + } + } + + public void testInterfacePrivateMethodsJava9() { + InterfaceWithPrivateMethods implementor = () -> 50; + assertEquals(50, implementor.implementedMethod()); + assertEquals(50, implementor.defaultMethod()); + assertEquals(42, implementor.staticPrivateMethod()); + } + + public void testAnonymousDiamondJava9() { + Supplier helloSupplier = new Supplier<>() { + @Override + public String get() { + return "hello"; + } + }; + assertEquals("hello", helloSupplier.get()); + } +} \ No newline at end of file diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java index 7ee0fb2b3dc..56e49ff62ae 100644 --- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java +++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java @@ -31,6 +31,7 @@ import com.google.gwt.dev.jjs.test.Java11Test; import com.google.gwt.dev.jjs.test.Java7Test; import com.google.gwt.dev.jjs.test.Java8Test; +import com.google.gwt.dev.jjs.test.Java9Test; import com.google.gwt.dev.jjs.test.JavaAccessFromJavaScriptTest; import com.google.gwt.dev.jjs.test.JsniConstructorTest; import com.google.gwt.dev.jjs.test.JsniDispatchTest; @@ -73,6 +74,7 @@ public static Test suite() { // Java8Test cannot be the first one in a suite. It uses a hack // to avoid executing if not in a Java 8+ environment. suite.addTestSuite(Java8Test.class); + suite.addTestSuite(Java9Test.class); suite.addTestSuite(Java10Test.class); suite.addTestSuite(Java11Test.class); suite.addTestSuite(JavaAccessFromJavaScriptTest.class); diff --git a/user/test/com/google/gwt/dev/jjs/Java9Test.gwt.xml b/user/test/com/google/gwt/dev/jjs/Java9Test.gwt.xml new file mode 100644 index 00000000000..ff61d0dc3c7 --- /dev/null +++ b/user/test/com/google/gwt/dev/jjs/Java9Test.gwt.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java index 5e4c773d756..ed2e5d20d27 100644 --- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java +++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java @@ -22,7 +22,7 @@ import com.google.gwt.junit.client.GWTTestCase; /** - * Dummy test case. Java8Test is super sourced so that GWT can be compiled by Java 6. + * Dummy test case. Java8Test was historically super sourced so that GWT can be compiled by Java 6. * * NOTE: Make sure this class has the same test methods of its supersourced variant. */ @@ -36,8 +36,8 @@ public String getModuleName() { @Override public void runTest() throws Throwable { - // Only run these tests if -sourceLevel 9 (or greater) is enabled. - if (isGwtSourceLevel9()) { + // Only run these tests if -sourceLevel 8 (or greater) is enabled. + if (isGwtSourceLevel8()) { super.runTest(); } } @@ -45,330 +45,318 @@ public void runTest() throws Throwable { public void testLambdaNoCapture() { // Make sure we are using the right Java8Test if the source compatibility level is set to Java 8 // or above. - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCaptureLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCaptureLocalWithInnerClass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCaptureLocalAndField() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCaptureLocalAndFieldWithInnerClass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testCompileLambdaCaptureOuterInnerField() throws Exception { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testStaticReferenceBinding() throws Exception { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testInstanceReferenceBinding() throws Exception { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testImplicitQualifierReferenceBinding() throws Exception { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testConstructorReferenceBinding() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testStaticInterfaceMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testArrayConstructorReference() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testArrayConstructorReferenceBoxed() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testVarArgsReferenceBinding() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testVarArgsPassthroughReferenceBinding() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testVarArgsPassthroughReferenceBindingProvidedArray() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testSuperReferenceExpression() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testQualifiedSuperReferenceExpression() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testSuperReferenceExpressionWithVarArgs() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testPrivateConstructorReference() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultInterfaceMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultInterfaceMethodVirtualUpRef() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testInterfaceWithDefaultMethodsInitialization() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultInterfaceMethodMultiple() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultMethodReference() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefenderMethodByInterfaceInstance() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultMethod_staticInitializer() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testThisRefInDefenderMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testClassImplementsTwoInterfacesWithSameDefenderMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testAbstractClassImplementsInterface() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testSuperRefInDefenderMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testSuperThisRefsInDefenderMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testNestedInterfaceClass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testBaseIntersectionCast() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testIntersectionCastWithLambdaExpr() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testIntersectionCastPolymorphism() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCaptureParameter() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingCaptureLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingInAnonymousCaptureLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingInMultipleMixedAnonymousCaptureLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingInMultipleMixedAnonymousCaptureLocal_withInterference() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingInMultipleMixedAnonymousCaptureLocalAndField() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingInMultipleAnonymousCaptureLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingCaptureField_InnerClassCapturingOuterClassVariable() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testInnerClassCaptureLocalFromOuterLambda() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaNestingCaptureField() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaMultipleNestingCaptureFieldAndLocal() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaMultipleNestingCaptureFieldAndLocalInnerClass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodRefWithSameName() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_fromInterfaces_left() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_fromInterfaces_right() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_superclass_left() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_superclass_right() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_defaultShadowsOverSyntheticAbstractStub() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMultipleDefaults_defaultShadowsOverDefaultOnSuperAbstract() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testInterfaceThis() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodReference_generics() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testNativeJsTypeWithStaticInitializer() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testJsVarargsLambda() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodReference_implementedInSuperclass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodReference_genericTypeParameters() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodReference_autoboxing() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testMethodReference_varargs() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testNativeJsOverlay_lambda() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaCapturingThis_onDefaultMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testJsFunction_withOverlay() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testFunctionalExpressionBridges() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testCorrectNaming() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testInterfaceWithOverlayAndNativeSubclass() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLocalClassConstructorReferenceInStaticMethod() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testDefaultMethodDevirtualizationOrder() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testVarargsFunctionalConversion() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testJSOLivenessSingleImplErasure() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaErasureCasts() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testLambdaBoxing() { - assertFalse(isGwtSourceLevel9()); - } - - public void testTryWithResourcesJava9() { - assertFalse(isGwtSourceLevel9()); - } - - public void testInterfacePrivateMethodsJava9() { - assertFalse(isGwtSourceLevel9()); - } - - public void testAnonymousDiamondJava9() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testImproperMethodResolution() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testIntersectionCastLambda() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } public void testIntersectionCastMethodReference() { - assertFalse(isGwtSourceLevel9()); + assertFalse(isGwtSourceLevel8()); } - private boolean isGwtSourceLevel9() { - return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA9) >= 0; + private boolean isGwtSourceLevel8() { + return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA8) >= 0; } } diff --git a/user/test/com/google/gwt/dev/jjs/test/Java9Test.java b/user/test/com/google/gwt/dev/jjs/test/Java9Test.java new file mode 100644 index 00000000000..951c8bbcaa0 --- /dev/null +++ b/user/test/com/google/gwt/dev/jjs/test/Java9Test.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.jjs.test; + +import com.google.gwt.dev.util.arg.SourceLevel; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.JUnitShell; +import com.google.gwt.junit.Platform; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * Dummy test case. Java9Test is super sourced so that GWT can be compiled by Java 8. + * + * NOTE: Make sure this class has the same test methods of its supersourced variant. + */ +@DoNotRunWith(Platform.Devel) +public class Java9Test extends GWTTestCase { + @Override + public String getModuleName() { + return "com.google.gwt.dev.jjs.Java9Test"; + } + + @Override + public void runTest() throws Throwable { + // Only run these tests if -sourceLevel 9 (or greater) is enabled. + if (isGwtSourceLevel9()) { + super.runTest(); + } + } + + public void testTryWithResourcesJava9() { + assertFalse(isGwtSourceLevel9()); + } + + public void testInterfacePrivateMethodsJava9() { + assertFalse(isGwtSourceLevel9()); + } + + public void testAnonymousDiamondJava9() { + assertFalse(isGwtSourceLevel9()); + } + + private boolean isGwtSourceLevel9() { + return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA9) >= 0; + } +} diff --git a/user/test/com/google/gwt/emultest/EmulJava10Suite.java b/user/test/com/google/gwt/emultest/EmulJava10Suite.java new file mode 100644 index 00000000000..e691477626a --- /dev/null +++ b/user/test/com/google/gwt/emultest/EmulJava10Suite.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest; + +import com.google.gwt.emultest.java10.util.OptionalDoubleTest; +import com.google.gwt.emultest.java10.util.OptionalIntTest; +import com.google.gwt.emultest.java10.util.OptionalLongTest; +import com.google.gwt.emultest.java10.util.OptionalTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** Test JRE emulations. */ +@RunWith(Suite.class) +@SuiteClasses({ + OptionalDoubleTest.class, + OptionalIntTest.class, + OptionalLongTest.class, + OptionalTest.class, +}) +public class EmulJava10Suite { +} diff --git a/user/test/com/google/gwt/emultest/EmulJava11Suite.java b/user/test/com/google/gwt/emultest/EmulJava11Suite.java new file mode 100644 index 00000000000..18260e0714f --- /dev/null +++ b/user/test/com/google/gwt/emultest/EmulJava11Suite.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest; + +import com.google.gwt.emultest.java11.util.OptionalDoubleTest; +import com.google.gwt.emultest.java11.util.OptionalIntTest; +import com.google.gwt.emultest.java11.util.OptionalLongTest; +import com.google.gwt.emultest.java11.util.OptionalTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** Test JRE emulations. */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + OptionalDoubleTest.class, + OptionalIntTest.class, + OptionalLongTest.class, + OptionalTest.class, +}) +public class EmulJava11Suite { +} diff --git a/user/test/com/google/gwt/emultest/EmulJava9Suite.java b/user/test/com/google/gwt/emultest/EmulJava9Suite.java new file mode 100644 index 00000000000..b2f81570e34 --- /dev/null +++ b/user/test/com/google/gwt/emultest/EmulJava9Suite.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest; + +import com.google.gwt.emultest.java9.util.stream.CollectorsTest; +import com.google.gwt.emultest.java9.util.stream.DoubleStreamTest; +import com.google.gwt.emultest.java9.util.stream.IntStreamTest; +import com.google.gwt.emultest.java9.util.stream.LongStreamTest; +import com.google.gwt.emultest.java9.util.stream.StreamTest; +import com.google.gwt.emultest.java9.util.ListTest; +import com.google.gwt.emultest.java9.util.MapTest; +import com.google.gwt.emultest.java9.util.OptionalDoubleTest; +import com.google.gwt.emultest.java9.util.OptionalIntTest; +import com.google.gwt.emultest.java9.util.OptionalLongTest; +import com.google.gwt.emultest.java9.util.OptionalTest; +import com.google.gwt.emultest.java9.util.SetTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** Test JRE emulations. */ +@RunWith(Suite.class) +@SuiteClasses({ + CollectorsTest.class, + DoubleStreamTest.class, + IntStreamTest.class, + LongStreamTest.class, + StreamTest.class, + ListTest.class, + MapTest.class, + OptionalDoubleTest.class, + OptionalIntTest.class, + OptionalLongTest.class, + OptionalTest.class, + SetTest.class +}) +public class EmulJava9Suite { +} diff --git a/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml b/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml index 550ef4bbf3e..85c3a4cf6b1 100644 --- a/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml +++ b/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml @@ -17,6 +17,9 @@ + + + diff --git a/user/test/com/google/gwt/emultest/java/lang/LongTest.java b/user/test/com/google/gwt/emultest/java/lang/LongTest.java index ebd007eec25..37fb27d6346 100644 --- a/user/test/com/google/gwt/emultest/java/lang/LongTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/LongTest.java @@ -254,6 +254,14 @@ public void testToBinaryString() { assertEquals( "1111111111111111111111111111111111111111111111111111111111111111", Long.toBinaryString(-1)); + + // Test int MAX+1 and int -1, as both are positive longs, but negative ints + assertEquals( + "10000000000000000000000000000000", + Long.toBinaryString(((long) Integer.MAX_VALUE) + 1)); + assertEquals( + "11111111111111111111111111111111", + Long.toBinaryString(4294967295L)); } public void testToHexString() { @@ -262,6 +270,8 @@ public void testToHexString() { assertEquals("1234500000000", Long.toHexString(0x1234500000000L)); assertEquals("fff1234500000000", Long.toHexString(0xFFF1234500000000L)); assertEquals("ffffffffffffffff", Long.toHexString(-1)); + assertEquals("80000000", Long.toHexString(((long) Integer.MAX_VALUE) + 1)); + assertEquals("ffffffff", Long.toHexString(4294967295L)); } public void testToOctalString() { @@ -270,6 +280,8 @@ public void testToOctalString() { assertEquals("1000000000000000000000", Long.toOctalString(Long.MIN_VALUE)); assertEquals("777777777777777777777", Long.toOctalString(Long.MAX_VALUE)); assertEquals("1777777777777777777777", Long.toOctalString(-1)); + assertEquals("20000000000", Long.toOctalString(((long) Integer.MAX_VALUE) + 1)); + assertEquals("37777777777", Long.toOctalString(4294967295L)); } public void testToString() { @@ -281,6 +293,8 @@ public void testToString() { assertEquals("80765", Long.toString(80765L)); assertEquals("-2147483648", Long.toString((long) Integer.MIN_VALUE)); assertEquals("2147483647", Long.toString((long) Integer.MAX_VALUE)); + assertEquals("2147483648", Long.toString(((long) Integer.MAX_VALUE) + 1)); + assertEquals("4294967295", Long.toString((long) 4294967295L)); assertEquals("-89000000005", Long.toString(-89000000005L)); assertEquals("89000000005", Long.toString(89000000005L)); assertEquals("-9223372036854775808", Long.toString(Long.MIN_VALUE)); diff --git a/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java b/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java index c1fc5de9916..93fd94b9e79 100644 --- a/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java +++ b/user/test/com/google/gwt/emultest/java/util/EmulTestBase.java @@ -57,6 +57,24 @@ public static void assertEquals(double[] expected, double[] actual) { Arrays.equals(expected, actual)); } + public static void assertNPE(String methodName, Runnable runnable) { + try { + runnable.run(); + fail("Expected NPE from calling " + methodName); + } catch (NullPointerException ignored) { + // expected + } + } + + public static void assertIAE(String methodName, Runnable runnable) { + try { + runnable.run(); + fail("Expected IAE from calling " + methodName); + } catch (IllegalArgumentException ignored) { + // expected + } + } + @Override public String getModuleName() { return "com.google.gwt.emultest.EmulSuite"; diff --git a/user/test/com/google/gwt/emultest/java10/util/OptionalDoubleTest.java b/user/test/com/google/gwt/emultest/java10/util/OptionalDoubleTest.java new file mode 100644 index 00000000000..ac39290ff1c --- /dev/null +++ b/user/test/com/google/gwt/emultest/java10/util/OptionalDoubleTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java10.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.NoSuchElementException; +import java.util.OptionalDouble; + +/** + * Tests for java.util.OptionalDouble Java 10 API emulation. + */ +public class OptionalDoubleTest extends EmulTestBase { + public void testOrElseThrow() { + try { + OptionalDouble.empty().orElseThrow(); + fail("Expected NoSuchElementException from empty Optional: orElseThrow"); + } catch (NoSuchElementException ignore) { + // expected + } + + double value = OptionalDouble.of(10.0).orElseThrow(); + assertEquals(10.0, value); + } +} diff --git a/user/test/com/google/gwt/emultest/java10/util/OptionalIntTest.java b/user/test/com/google/gwt/emultest/java10/util/OptionalIntTest.java new file mode 100644 index 00000000000..59b39539877 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java10/util/OptionalIntTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java10.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.NoSuchElementException; +import java.util.OptionalInt; + +/** + * Tests for java.util.OptionalInt Java 10 API emulation. + */ +public class OptionalIntTest extends EmulTestBase { + public void testOrElseThrow() { + try { + OptionalInt.empty().orElseThrow(); + fail("Expected NoSuchElementException from empty Optional: orElseThrow"); + } catch (NoSuchElementException ignore) { + // expected + } + + int value = OptionalInt.of(10).orElseThrow(); + assertEquals(10, value); + } +} diff --git a/user/test/com/google/gwt/emultest/java10/util/OptionalLongTest.java b/user/test/com/google/gwt/emultest/java10/util/OptionalLongTest.java new file mode 100644 index 00000000000..1fb35891bd7 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java10/util/OptionalLongTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java10.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.NoSuchElementException; +import java.util.OptionalLong; + +/** + * Tests for java.util.OptionalLong Java 10 API emulation. + */ +public class OptionalLongTest extends EmulTestBase { + public void testOrElseThrow() { + try { + OptionalLong.empty().orElseThrow(); + fail("Expected NoSuchElementException from empty Optional: orElseThrow"); + } catch (NoSuchElementException ignore) { + // expected + } + + long value = OptionalLong.of(10L).orElseThrow(); + assertEquals(10L, value); + } +} diff --git a/user/test/com/google/gwt/emultest/java10/util/OptionalTest.java b/user/test/com/google/gwt/emultest/java10/util/OptionalTest.java new file mode 100644 index 00000000000..d2635993885 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java10/util/OptionalTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java10.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.NoSuchElementException; +import java.util.Optional; + +/** + * Tests for java.util.Optional Java 10 API emulation. + */ +public class OptionalTest extends EmulTestBase { + @SuppressWarnings("OptionalOfRedundantMethod") + public void testOrElseThrow() { + try { + Optional.empty().orElseThrow(); + fail("Expected NoSuchElementException from empty Optional: orElseThrow"); + } catch (NoSuchElementException ignore) { + // expected + } + + String value = Optional.of("value").orElseThrow(); + assertEquals("value", value); + } +} diff --git a/user/test/com/google/gwt/emultest/java11/util/OptionalDoubleTest.java b/user/test/com/google/gwt/emultest/java11/util/OptionalDoubleTest.java new file mode 100644 index 00000000000..5a2779bb213 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java11/util/OptionalDoubleTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java11.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalDouble; + +/** + * Tests for java.util.OptionalDouble Java 11 API emulation. + */ +public class OptionalDoubleTest extends EmulTestBase { + public void testIsEmpty() { + assertTrue(OptionalDouble.empty().isEmpty()); + assertFalse(OptionalDouble.of(78.9).isEmpty()); + } +} diff --git a/user/test/com/google/gwt/emultest/java11/util/OptionalIntTest.java b/user/test/com/google/gwt/emultest/java11/util/OptionalIntTest.java new file mode 100644 index 00000000000..2c909b8e962 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java11/util/OptionalIntTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java11.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalInt; + +/** + * Tests for java.util.OptionalInt Java 11 API emulation. + */ +public class OptionalIntTest extends EmulTestBase { + public void testIsEmpty() { + assertTrue(OptionalInt.empty().isEmpty()); + assertFalse(OptionalInt.of(456).isEmpty()); + } +} diff --git a/user/test/com/google/gwt/emultest/java11/util/OptionalLongTest.java b/user/test/com/google/gwt/emultest/java11/util/OptionalLongTest.java new file mode 100644 index 00000000000..54c61176511 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java11/util/OptionalLongTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java11.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalLong; + +/** + * Tests for java.util.OptionalLong Java 11 API emulation. + */ +public class OptionalLongTest extends EmulTestBase { + public void testIsEmpty() { + assertTrue(OptionalLong.empty().isEmpty()); + assertFalse(OptionalLong.of(123L).isEmpty()); + } +} diff --git a/user/test/com/google/gwt/emultest/java11/util/OptionalTest.java b/user/test/com/google/gwt/emultest/java11/util/OptionalTest.java new file mode 100644 index 00000000000..80586871cd7 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java11/util/OptionalTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java11.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Optional; + +/** + * Tests for java.util.Optional Java 11 API emulation. + */ +public class OptionalTest extends EmulTestBase { + public void testIsEmpty() { + assertTrue(Optional.empty().isEmpty()); + assertFalse(Optional.of(this).isEmpty()); + } +} diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/CollectorsTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/CollectorsTest.java index bfafad0f990..28fbf0da71c 100644 --- a/user/test/com/google/gwt/emultest/java8/util/stream/CollectorsTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/stream/CollectorsTest.java @@ -466,7 +466,7 @@ public void testSet() { * This method attempts to apply a collector to items as a stream might do, so that we can simply * verify the output. Taken from the Collector class's javadoc. */ - private static void applyItems( + public static void applyItems( R expected, Collector collector, T t1, T t2, BiPredicate equals) { assertTrue( "failed without splitting", @@ -479,7 +479,7 @@ private static void applyItems( * This method attempts to apply a collector to items as a stream might do, so that we * can simply verify the output. Taken from the Collector class's javadoc. */ - private static void applyItems(R expected, Collector collector, T t1, T t2) { + public static void applyItems(R expected, Collector collector, T t1, T t2) { applyItems(expected, collector, t1, t2, Object::equals); } diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java index 68140c7ada3..1cc6fbce97a 100644 --- a/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/stream/DoubleStreamTest.java @@ -17,6 +17,8 @@ package com.google.gwt.emultest.java8.util.stream; import com.google.gwt.emultest.java.util.EmulTestBase; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; import java.util.ArrayList; import java.util.Arrays; @@ -113,10 +115,30 @@ public void testConcat() { assertEquals(Arrays.asList("first", "second"), closed); } + // Java8 has the same bug that GWT previously had here, so we're excluding Java8 from running + // this test. Presently, legacy dev mode is the only way to actually run this test using the + // JVM's own implementation, so marking this DoNotRunWith(Devel) is sufficient to avoid this. + @DoNotRunWith(Platform.Devel) public void testIterate() { assertEquals( new double[] {10d, 11d, 12d, 13d, 14d}, DoubleStream.iterate(0d, l -> l + 1d).skip(10).limit(5).toArray()); + + // Infinite stream, verify that it is correctly limited by a downstream step + assertEquals( + new double[] {0, 1, 2, 3, 4}, + DoubleStream.iterate(0, i -> i + 1).limit(5).toArray()); + + // Check that the function is called the correct number of times + int[] calledCount = {0}; + double[] array = DoubleStream.iterate(0, val -> { + calledCount[0]++; + return val + 1; + }).limit(5).toArray(); + // Verify that the function was called for each value after the seed + assertEquals(array.length - 1, calledCount[0]); + // Sanity check the values returned + assertEquals(new double[] {0, 1, 2, 3, 4}, array); } public void testGenerate() { diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java index 5fea722124a..d9b4ebb8304 100644 --- a/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/stream/IntStreamTest.java @@ -17,6 +17,8 @@ package com.google.gwt.emultest.java8.util.stream; import com.google.gwt.emultest.java.util.EmulTestBase; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; import java.util.ArrayList; import java.util.Arrays; @@ -112,10 +114,30 @@ public void testConcat() { assertEquals(Arrays.asList("first", "second"), closed); } + // Java8 has the same bug that GWT previously had here, so we're excluding Java8 from running + // this test. Presently, legacy dev mode is the only way to actually run this test using the + // JVM's own implementation, so marking this DoNotRunWith(Devel) is sufficient to avoid this. + @DoNotRunWith(Platform.Devel) public void testIterate() { assertEquals( new int[] {10, 11, 12, 13, 14}, IntStream.iterate(0, i -> i + 1).skip(10).limit(5).toArray()); + + // Infinite stream, verify that it is correctly limited by a downstream step + assertEquals( + new int[] {0, 1, 2, 3, 4}, + IntStream.iterate(0, i -> i + 1).limit(5).toArray()); + + // Check that the function is called the correct number of times + int[] calledCount = {0}; + int[] array = IntStream.iterate(0, val -> { + calledCount[0]++; + return val + 1; + }).limit(5).toArray(); + // Verify that the function was called for each value after the seed + assertEquals(array.length - 1, calledCount[0]); + // Sanity check the values returned + assertEquals(new int[] {0, 1, 2, 3, 4}, array); } public void testGenerate() { diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java index ff764775474..6297afdf71d 100644 --- a/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/stream/LongStreamTest.java @@ -17,6 +17,8 @@ package com.google.gwt.emultest.java8.util.stream; import com.google.gwt.emultest.java.util.EmulTestBase; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; import java.util.ArrayList; import java.util.Arrays; @@ -111,10 +113,30 @@ public void testConcat() { assertEquals(Arrays.asList("first", "second"), closed); } + // Java8 has the same bug that GWT previously had here, so we're excluding Java8 from running + // this test. Presently, legacy dev mode is the only way to actually run this test using the + // JVM's own implementation, so marking this DoNotRunWith(Devel) is sufficient to avoid this. + @DoNotRunWith(Platform.Devel) public void testIterate() { assertEquals( new long[] {10L, 11L, 12L, 13L, 14L}, LongStream.iterate(0L, l -> l + 1L).skip(10).limit(5).toArray()); + + // Infinite stream, verify that it is correctly limited by a downstream step + assertEquals( + new long[] {0, 1, 2, 3, 4}, + LongStream.iterate(0, i -> i + 1).limit(5).toArray()); + + // Check that the function is called the correct number of times + int[] calledCount = {0}; + long[] array = LongStream.iterate(0, val -> { + calledCount[0]++; + return val + 1; + }).limit(5).toArray(); + // Verify that the function was called for each value after the seed + assertEquals(array.length - 1, calledCount[0]); + // Sanity check the values returned + assertEquals(new long[] {0, 1, 2, 3, 4}, array); } public void testGenerate() { diff --git a/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java b/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java index 08c4c897398..f1aea123f02 100644 --- a/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java +++ b/user/test/com/google/gwt/emultest/java8/util/stream/StreamTest.java @@ -132,6 +132,22 @@ public void testIterate() { assertEquals( new Integer[] {10, 11, 12, 13, 14}, Stream.iterate(0, i -> i + 1).skip(10).limit(5).toArray(Integer[]::new)); + + // Infinite stream, verify that it is correctly limited by a downstream step + assertEquals( + new Integer[] {0, 1, 2, 3, 4}, + Stream.iterate(0, i -> i + 1).limit(5).toArray()); + + // Check that the function is called the correct number of times + int[] calledCount = {0}; + Integer[] array = Stream.iterate(0, val -> { + calledCount[0]++; + return val + 1; + }).limit(5).toArray(Integer[]::new); + // Verify that the function was called for each value after the seed + assertEquals(array.length - 1, calledCount[0]); + // Sanity check the values returned + assertEquals(new Integer[] {0, 1, 2, 3, 4}, array); } public void testGenerate() { diff --git a/user/test/com/google/gwt/emultest/java9/util/ListTest.java b/user/test/com/google/gwt/emultest/java9/util/ListTest.java new file mode 100644 index 00000000000..d1487a477f6 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/ListTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Iterator; +import java.util.List; + +/** + * Tests for java.util.List Java 9 API emulation. + */ +public class ListTest extends EmulTestBase { + + public void testOf() { + assertIsImmutableListOf(List.of()); + assertIsImmutableListOf(List.of("a"), "a"); + assertIsImmutableListOf( + List.of("a", "b"), + "a", "b" + ); + assertIsImmutableListOf( + List.of("a", "b", "c"), + "a", "b", "c" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d"), + "a", "b", "c", "d" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e"), + "a", "b", "c", "d", "e" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f"), + "a", "b", "c", "d", "e", "f" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f", "g"), + "a", "b", "c", "d", "e", "f", "g" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f", "g", "h"), + "a", "b", "c", "d", "e", "f", "g", "h" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + "a", "b", "c", "d", "e", "f", "g", "h", "i" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" + ); + assertIsImmutableListOf( + List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" + ); + + // ensure that NPE is thrown if a value is null + assertNPE("of", () -> List.of((String) null)); + assertNPE("of", () -> List.of("a", null)); + assertNPE("of", () -> List.of("a", "b", null)); + assertNPE("of", () -> List.of("a", "b", "c", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", "f", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", "f", "g", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", "f", "g", "h", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null)); + assertNPE("of", () -> List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", null)); + } + + protected static void assertIsImmutableListOf(List list, String... contents) { + assertEquals(contents, list); + + // quick test that the list impl is sane + if (contents.length == 0) { + assertFalse(list.iterator().hasNext()); + } else { + Iterator itr = list.iterator(); + assertTrue(itr.hasNext()); + assertEquals(contents[0], itr.next()); + assertEquals(contents.length > 1, itr.hasNext()); + } + + // quick check that the list is immutable + try { + list.add("another item"); + fail("List should be unmodifiable: add(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + try { + list.remove(0); + fail("List should be unmodifiable: remove(int)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + // if any, remove an item actually in the list + if (contents.length > 0) { + // Without any items, remove(T) defaults to iterating items present, so we only test from + // present items + try { + list.remove(contents[0]); + fail("List should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + } + + // Remove an item that will not be in the list + try { + list.remove("not present"); + fail("List should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + try { + list.clear(); + fail("List should be unmodifiable: clear()"); + } catch (UnsupportedOperationException ignored) { + // success + } + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/MapTest.java b/user/test/com/google/gwt/emultest/java9/util/MapTest.java new file mode 100644 index 00000000000..9d1356f9ca3 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/MapTest.java @@ -0,0 +1,218 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Map; + +/** + * Tests for java.util.Map Java 9 API emulation. + */ +public class MapTest extends EmulTestBase { + + public void testOf() { + assertIsImmutableMapOf(Map.of()); + assertIsImmutableMapOf(Map.of("a", 1), "a"); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2), + "a", "b" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3), + "a", "b", "c" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4), + "a", "b", "c", "d" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5), + "a", "b", "c", "d", "e" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6), + "a", "b", "c", "d", "e", "f" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7), + "a", "b", "c", "d", "e", "f", "g" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8), + "a", "b", "c", "d", "e", "f", "g", "h" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9), + "a", "b", "c", "d", "e", "f", "g", "h", "i" + ); + assertIsImmutableMapOf( + Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9, "j", 10), + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" + ); + + // ensure NullPointerException if either key or value are null for any param + assertNPE("Map.of(1)", () -> Map.of(null, 1)); + assertNPE("Map.of(1)", () -> Map.of("a", null)); + assertNPE("Map.of(2)", () -> Map.of("a", 1, null, 2)); + assertNPE("Map.of(2)", () -> Map.of("a", 1, "b", null)); + assertNPE("Map.of(3)", () -> Map.of("a", 1, "b", 2, null, 3)); + assertNPE("Map.of(3)", () -> Map.of("a", 1, "b", 2, "c", null)); + assertNPE("Map.of(4)", () -> Map.of("a", 1, "b", 2, "c", 3, null, 4)); + assertNPE("Map.of(4)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", null)); + assertNPE("Map.of(5)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, null, 5)); + assertNPE("Map.of(5)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", null)); + assertNPE("Map.of(6)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, null, 6)); + assertNPE("Map.of(6)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", null)); + assertNPE("Map.of(7)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + null, 7)); + assertNPE("Map.of(7)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", null)); + assertNPE("Map.of(8)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, null, 8)); + assertNPE("Map.of(8)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, "h", null)); + assertNPE("Map.of(9)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, "h", 8, null, 9)); + assertNPE("Map.of(9)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, "h", 8, "i", null)); + assertNPE("Map.of(10)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, "h", 8, "i", 9, null, 10)); + assertNPE("Map.of(10)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, + "g", 7, "h", 8, "i", 9, "j", null)); + + // ensure IllegalArgumentException if any key is repeated + assertIAE("Map.of(2)", () -> Map.of("a", 1, "a", 2)); + assertIAE("Map.of(3)", () -> Map.of("a", 1, "b", 2, "a", 3)); + assertIAE("Map.of(4)", () -> Map.of("a", 1, "b", 2, "c", 3, "a", 4)); + assertIAE("Map.of(5)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "a", 5)); + assertIAE("Map.of(6)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "a", 6)); + assertIAE("Map.of(7)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "a", 7)); + assertIAE("Map.of(8)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, + "a", 8)); + assertIAE("Map.of(9)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, + "h", 8, "a", 9)); + assertIAE("Map.of(10)", () -> Map.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, + "h", 8, "i", 9, "a", 10)); + } + + protected static void assertIsImmutableMapOf(Map map, String... contents) { + assertEquals(contents.length, map.size()); + for (int i = 0; i < contents.length; i++) { + assertTrue(map.containsKey(contents[i])); + assertFalse(map.containsKey(contents[i] + "nope")); + assertEquals(i + 1, (int) map.get(contents[i])); + } + + // quick check that the map is immutable + try { + map.put("another item", 1); + fail("Set should be unmodifiable: add(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + if (contents.length > 1) { + // Without any items, remove(T) defaults to iterating items present, so we only test from + // present items + try { + map.remove(contents[0]); + fail("Map should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + } + + try { + map.remove("not found"); + fail("Map should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + try { + map.clear(); + fail("Set should be unmodifiable: clear()"); + } catch (UnsupportedOperationException ignored) { + // expected + } + } + + public void testEntry() { + Map.Entry entry = Map.entry("a", "b"); + + assertEquals("a", entry.getKey()); + assertEquals("b", entry.getValue()); + + try { + entry.setValue("z"); + fail("Entry should be immutable: setValue"); + } catch (UnsupportedOperationException ignore) { + // expected + } + + assertNPE("Map.entry", () -> { + Map.entry(null, "value"); + }); + assertNPE("Map.entry", () -> { + Map.entry("key", null); + }); + } + + @SuppressWarnings("DuplicateMapKeys") + public void testOfEntries() { + Map map = Map.ofEntries( + Map.entry("a", 1), + Map.entry("b", 2) + ); + + assertIsImmutableMapOf(map, "a", "b"); + + // ensure NullPointerException if any entry is null, if any key is null, or value is null + assertNPE("Map.ofEntries", () -> { + Map.ofEntries( + Map.entry("a", "b"), + null + ); + }); + assertNPE("Map.ofEntries", () -> { + Map.ofEntries( + Map.entry("a", "b"), + Map.entry("c", null) + ); + }); + assertNPE("Map.ofEntries", () -> { + Map.ofEntries( + Map.entry("a", "b"), + Map.entry(null, "d") + ); + }); + + // ensure IllegalArgumentException if any pair has the same key (same or different value) + assertIAE("Map.ofEntries", () -> { + Map.ofEntries( + Map.entry("a", "b"), + Map.entry("a", "b") + ); + }); + assertIAE("Map.ofEntries", () -> { + Map.ofEntries( + Map.entry("a", "b"), + Map.entry("a", "c") + ); + }); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/OptionalDoubleTest.java b/user/test/com/google/gwt/emultest/java9/util/OptionalDoubleTest.java new file mode 100644 index 00000000000..6c1aaf294e9 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/OptionalDoubleTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalDouble; +import java.util.stream.Stream; + +/** + * Tests for java.util.OptionalDouble Java 9 API emulation. + */ +public class OptionalDoubleTest extends EmulTestBase { + public void testIfPresentOrElse() { + int[] called = {0}; + OptionalDouble.of(10.0).ifPresentOrElse(value -> { + assertEquals(10.0, value); + called[0]++; + }, () -> { + fail("should not call empty action"); + }); + assertEquals(1, called[0]); + called[0] = 0; + OptionalDouble.empty().ifPresentOrElse(ignore -> { + fail("Should not call present action"); + }, () -> called[0]++); + assertEquals(1, called[0]); + } + + public void testStream() { + assertEquals(0, OptionalDouble.empty().stream().count()); + assertEquals(1, OptionalDouble.of(10.0).stream().count()); + + assertEquals( + new double[] {10.0, 100.0, 1000.0}, + Stream.of( + OptionalDouble.of(10.0), + OptionalDouble.empty(), + OptionalDouble.of(100.0), + OptionalDouble.of(1000.0) + ).flatMapToDouble(OptionalDouble::stream).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/OptionalIntTest.java b/user/test/com/google/gwt/emultest/java9/util/OptionalIntTest.java new file mode 100644 index 00000000000..b6fa3783451 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/OptionalIntTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalInt; +import java.util.stream.Stream; + +/** + * Tests for java.util.OptionalInt Java 9 API emulation. + */ +public class OptionalIntTest extends EmulTestBase { + public void testIfPresentOrElse() { + int[] called = {0}; + OptionalInt.of(10).ifPresentOrElse(value -> { + assertEquals(10, value); + called[0]++; + }, () -> { + fail("should not call empty action"); + }); + assertEquals(1, called[0]); + called[0] = 0; + OptionalInt.empty().ifPresentOrElse(ignore -> { + fail("Should not call present action"); + }, () -> called[0]++); + assertEquals(1, called[0]); + } + + public void testStream() { + assertEquals(0, OptionalInt.empty().stream().count()); + assertEquals(1, OptionalInt.of(10).stream().count()); + + assertEquals( + new int[] {10, 100, 1000}, + Stream.of( + OptionalInt.of(10), + OptionalInt.empty(), + OptionalInt.of(100), + OptionalInt.of(1000) + ).flatMapToInt(OptionalInt::stream).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/OptionalLongTest.java b/user/test/com/google/gwt/emultest/java9/util/OptionalLongTest.java new file mode 100644 index 00000000000..5bb4ed670d8 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/OptionalLongTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.OptionalLong; +import java.util.stream.Stream; + +/** + * Tests for java.util.OptionalLong Java 9 API emulation. + */ +public class OptionalLongTest extends EmulTestBase { + public void testIfPresentOrElse() { + int[] called = {0}; + OptionalLong.of(10).ifPresentOrElse(value -> { + assertEquals(10, value); + called[0]++; + }, () -> { + fail("should not call empty action"); + }); + assertEquals(1, called[0]); + called[0] = 0; + OptionalLong.empty().ifPresentOrElse(ignore -> { + fail("Should not call present action"); + }, () -> called[0]++); + assertEquals(1, called[0]); + } + + public void testStream() { + assertEquals(0, OptionalLong.empty().stream().count()); + assertEquals(1, OptionalLong.of(10).stream().count()); + + assertEquals( + new long[] {10, 100, 1000}, + Stream.of( + OptionalLong.of(10), + OptionalLong.empty(), + OptionalLong.of(100), + OptionalLong.of(1000) + ).flatMapToLong(OptionalLong::stream).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/OptionalTest.java b/user/test/com/google/gwt/emultest/java9/util/OptionalTest.java new file mode 100644 index 00000000000..e7befa7724a --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/OptionalTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Tests for java.util.Optional Java 9 API emulation. + */ +public class OptionalTest extends EmulTestBase { + public void testIfPresentOrElse() { + int[] called = {0}; + Optional.of("value").ifPresentOrElse(value -> { + assertEquals("value", value); + called[0]++; + }, () -> { + fail("should not call empty action"); + }); + assertEquals(1, called[0]); + called[0] = 0; + Optional.empty().ifPresentOrElse(ignore -> { + fail("Should not call present action"); + }, () -> called[0]++); + assertEquals(1, called[0]); + } + + public void testOr() { + Optional or = Optional.of("value").or(() -> Optional.of("replacement")); + assertTrue(or.isPresent()); + assertEquals("value", or.get()); + + or = Optional.empty().or(() -> Optional.of("replacement")); + assertTrue(or.isPresent()); + assertEquals("replacement", or.get()); + + or = Optional.of("value").or(() -> Optional.empty()); + assertTrue(or.isPresent()); + assertEquals("value", or.get()); + + or = Optional.empty().or(() -> Optional.empty()); + assertFalse(or.isPresent()); + + Optional empty = Optional.empty(); + Optional present = Optional.of("asdf"); + assertNPE("empty().or(null)", () -> { + empty.or(null); + }); + assertNPE("present.or(null)", () -> { + present.or(null); + }); + assertNPE("empty.or(() -> null)", () -> { + empty.or(() -> null); + }); + } + + public void testStream() { + assertEquals(0, Optional.empty().stream().count()); + assertEquals(1, Optional.of("foo").stream().count()); + + assertEquals( + new String[] {"a", "b", "c"}, + Stream.of( + Optional.of("a"), + Optional.empty(), + Optional.of("b"), + Optional.of("c") + ).flatMap(Optional::stream).toArray(String[]::new) + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/SetTest.java b/user/test/com/google/gwt/emultest/java9/util/SetTest.java new file mode 100644 index 00000000000..88b1b1bad5a --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/SetTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; + +/** + * Tests for java.util.Set Java 9 API emulation. + */ +public class SetTest extends EmulTestBase { + + public void testOf() { + assertIsImmutableSetOf(Set.of()); + assertIsImmutableSetOf(Set.of("a"), "a"); + assertIsImmutableSetOf( + Set.of("a", "b"), + "a", "b" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c"), + "a", "b", "c" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d"), + "a", "b", "c", "d" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e"), + "a", "b", "c", "d", "e" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f"), + "a", "b", "c", "d", "e", "f" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f", "g"), + "a", "b", "c", "d", "e", "f", "g" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f", "g", "h"), + "a", "b", "c", "d", "e", "f", "g", "h" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + "a", "b", "c", "d", "e", "f", "g", "h", "i" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" + ); + assertIsImmutableSetOf( + Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" + ); + + // ensure NPE if any element is null + assertNPE("Set.of(1)", () -> Set.of((String) null)); + assertNPE("Set.of(2)", () -> Set.of("a", null)); + assertNPE("Set.of(3)", () -> Set.of("a", "b", null)); + assertNPE("Set.of(4)", () -> Set.of("a", "b", "c", null)); + assertNPE("Set.of(5)", () -> Set.of("a", "b", "c", "d", null)); + assertNPE("Set.of(6)", () -> Set.of("a", "b", "c", "d", "e", null)); + assertNPE("Set.of(7)", () -> Set.of("a", "b", "c", "d", "e", "f", null)); + assertNPE("Set.of(8)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", null)); + assertNPE("Set.of(9)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", null)); + assertNPE("Set.of(10)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null)); + assertNPE("Set.of(...)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", null)); + + // ensure IAE if any element is duplicated + assertIAE("Set.of(2)", () -> Set.of("a", "a")); + assertIAE("Set.of(3)", () -> Set.of("a", "b", "a")); + assertIAE("Set.of(4)", () -> Set.of("a", "b", "c", "a")); + assertIAE("Set.of(5)", () -> Set.of("a", "b", "c", "d", "a")); + assertIAE("Set.of(6)", () -> Set.of("a", "b", "c", "d", "e", "a")); + assertIAE("Set.of(7)", () -> Set.of("a", "b", "c", "d", "e", "f", "a")); + assertIAE("Set.of(8)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "a")); + assertIAE("Set.of(9)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", "a")); + assertIAE("Set.of(10)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "a")); + assertIAE("Set.of(...)", () -> Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "a")); + } + + protected static void assertIsImmutableSetOf(Set set, String... contents) { + assertEquals(contents.length, set.size()); + for (int i = 0; i < contents.length; i++) { + assertTrue(set.contains(contents[i])); + assertFalse(set.contains(contents[i] + "nope")); + } + + // quick test that the set impl is sane, aside from the above + if (contents.length == 0) { + assertFalse(set.iterator().hasNext()); + } else { + Iterator itr = set.iterator(); + assertTrue(itr.hasNext()); + + assertContains(contents, itr.next()); + + assertEquals(contents.length > 1, itr.hasNext()); + } + + // quick check that the set is immutable + try { + set.add("another item"); + fail("Set should be unmodifiable: add(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + // if any, remove an item actually in the set + if (contents.length > 1) { + // Without any items, remove(T) defaults to iterating items present, so we only test from + // present items + try { + set.remove(contents[0]); + fail("Set should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + } + + // Remove an item that will not be in the set + try { + set.remove("not present"); + fail("Set should be unmodifiable: remove(T)"); + } catch (UnsupportedOperationException ignored) { + // success + } + + try { + set.clear(); + fail("Set should be unmodifiable: clear()"); + } catch (UnsupportedOperationException ignored) { + // success + } + } + + private static void assertContains(String[] contents, String value) { + for (String item : contents) { + if (item.equals(value)) { + return; + } + } + fail("Failed to find '" + value + "' in " + Arrays.toString(contents)); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/stream/CollectorsTest.java b/user/test/com/google/gwt/emultest/java9/util/stream/CollectorsTest.java new file mode 100644 index 00000000000..1e7adea0814 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/stream/CollectorsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util.stream; + +import static com.google.gwt.emultest.java8.util.stream.CollectorsTest.applyItems; +import static java.util.stream.Collectors.filtering; +import static java.util.stream.Collectors.flatMapping; +import static java.util.stream.Collectors.toList; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collector; + +/** + * Tests for java.util.stream.Collectors Java 9 API emulation. + */ +public class CollectorsTest extends EmulTestBase { + + public void testFlatMapping() { + // since applyItems tests the same inputs multiple times, we need fresh stream instances as they can't be reused + Collector, ?, List> flatMapping = flatMapping(Collection::stream, + toList()); + applyItems(Arrays.asList("a", "b"), flatMapping, Collections.singletonList("a"), + Collections.singletonList("b")); + applyItems(Arrays.asList("c", "d"), flatMapping, Collections.emptyList(), Arrays.asList("c", "d")); + + Collector, ?, List> flatMappingToNull = flatMapping(items -> { + if (items.size() % 2 == 0) { + // Return null instead of empty + return null; + } + return items.stream(); + }, toList()); + applyItems(Arrays.asList("a"), flatMappingToNull, Arrays.asList("a"), Arrays.asList("b", "c")); + } + + public void testFiltering() { + Collector> filtering = filtering(s -> s.equals("a"), toList()); + applyItems(Collections.singletonList("a"), filtering, "a", "b"); + applyItems(Collections.emptyList(), filtering, "c", "d"); + applyItems(Arrays.asList("a", "a"), filtering, "a", "a"); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/stream/DoubleStreamTest.java b/user/test/com/google/gwt/emultest/java9/util/stream/DoubleStreamTest.java new file mode 100644 index 00000000000..d3b1585567f --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/stream/DoubleStreamTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.stream.DoubleStream; + +/** + * Tests for java.util.stream.DoubleStream Java 9 API emulation. + */ +public class DoubleStreamTest extends EmulTestBase { + public void testIterate() { + // Terminating stream, based on a predicate + assertEquals( + new double[] {10, 11, 12, 13, 14}, + DoubleStream.iterate(0, i -> i < 15, i -> i + 1).skip(10).toArray()); + + // Check that the functions are called the correct number of times with a hasNext limiting + // stream size + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + double[] array = DoubleStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).toArray(); + + // Verify that the next was called for each value after the seed (plus one for testing that + // the stream is over) + assertEquals(array.length, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value (plus one as above) + assertEquals(array.length + 1, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new double[]{0, 1, 2, 3, 4}, array); + } + + // Same test repeated, but instead limit() will stop the stream from continuing + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + double[] array = DoubleStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).limit(3).toArray(); + + // Verify that the next was called for each value after the seed + assertEquals(array.length - 1, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value + assertEquals(array.length, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new double[]{0, 1, 2}, array); + } + } + + public void testTakeWhile() { + assertEquals( + new double[] {1, 2}, + DoubleStream.of(1, 2, 3, 4, 5).takeWhile(i -> i < 3).toArray() + ); + assertEquals(0, DoubleStream.of(1, 2, 3, 4, 5).takeWhile(i -> i > 2).count()); + + assertEquals( + new double[] {0, 1, 2, 3, 4}, + DoubleStream.iterate(0, i -> i + 1).takeWhile(i -> i < 5).toArray() + ); + } + + public void testDropWhile() { + assertEquals( + new double[] {3, 4, 5}, + DoubleStream.of(1, 2, 3, 4, 5).dropWhile(i -> i < 3).toArray() + ); + assertEquals( + new double[] {1, 2, 3, 4, 5}, + DoubleStream.of(1, 2, 3, 4, 5).dropWhile(i -> i > 2).toArray() + ); + + // pass an infinite stream to dropWhile, ensure it handles it + assertEquals( + new double[] {5, 6, 7, 8, 9}, + DoubleStream.iterate(0, i -> i + 1).dropWhile(i -> i < 5).limit(5).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/stream/IntStreamTest.java b/user/test/com/google/gwt/emultest/java9/util/stream/IntStreamTest.java new file mode 100644 index 00000000000..ced92ef0d76 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/stream/IntStreamTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.stream.IntStream; + +/** + * Tests for java.util.stream.IntStream Java 9 API emulation. + */ +public class IntStreamTest extends EmulTestBase { + public void testIterate() { + assertEquals( + new int[] {10, 11, 12, 13, 14}, + IntStream.iterate(0, i -> i < 15, i -> i + 1).skip(10).toArray()); + + // Check that the functions are called the correct number of times with a hasNext limiting + // stream size + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + int[] array = IntStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).toArray(); + + // Verify that the next was called for each value after the seed (plus one for testing that + // the stream is over) + assertEquals(array.length, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value (plus one as above) + assertEquals(array.length + 1, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new int[]{0, 1, 2, 3, 4}, array); + } + + // Same test repeated, but instead limit() will stop the stream from continuing + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + int[] array = IntStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).limit(3).toArray(); + + // Verify that the next was called for each value after the seed + assertEquals(array.length - 1, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value + assertEquals(array.length, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new int[]{0, 1, 2}, array); + } + } + + public void testTakeWhile() { + assertEquals( + new int[] {1, 2}, + IntStream.of(1, 2, 3, 4, 5).takeWhile(i -> i < 3).toArray() + ); + assertEquals(0, IntStream.of(1, 2, 3, 4, 5).takeWhile(i -> i > 2).count()); + + assertEquals( + new int[] {0, 1, 2, 3, 4}, + IntStream.iterate(0, i -> i + 1).takeWhile(i -> i < 5).toArray() + ); + } + + public void testDropWhile() { + assertEquals( + new int[] {3, 4, 5}, + IntStream.of(1, 2, 3, 4, 5).dropWhile(i -> i < 3).toArray() + ); + assertEquals( + new int[] {1, 2, 3, 4, 5}, + IntStream.of(1, 2, 3, 4, 5).dropWhile(i -> i > 2).toArray() + ); + + // pass an infinite stream to dropWhile, ensure it handles it + assertEquals( + new int[] {5, 6, 7, 8, 9}, + IntStream.iterate(0, i -> i + 1).dropWhile(i -> i < 5).limit(5).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/stream/LongStreamTest.java b/user/test/com/google/gwt/emultest/java9/util/stream/LongStreamTest.java new file mode 100644 index 00000000000..26ff6b66757 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/stream/LongStreamTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.stream.LongStream; + +/** + * Tests for java.util.stream.IntStream Java 9 API emulation. + */ +public class LongStreamTest extends EmulTestBase { + public void testIterate() { + assertEquals( + new long[] {10, 11, 12, 13, 14}, + LongStream.iterate(0, i -> i < 15, i -> i + 1).skip(10).toArray()); + + // Check that the functions are called the correct number of times with a hasNext limiting + // stream size + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + long[] array = LongStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).toArray(); + + // Verify that the next was called for each value after the seed (plus one for testing that + // the stream is over) + assertEquals(array.length, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value (plus one as above) + assertEquals(array.length + 1, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new long[]{0, 1, 2, 3, 4}, array); + } + + // Same test repeated, but instead limit() will stop the stream from continuing + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + long[] array = LongStream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).limit(3).toArray(); + + // Verify that the next was called for each value after the seed + assertEquals(array.length - 1, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value + assertEquals(array.length, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new long[]{0, 1, 2}, array); + } + } + + public void testTakeWhile() { + assertEquals( + new long[] {1, 2}, + LongStream.of(1, 2, 3, 4, 5).takeWhile(i -> i < 3).toArray() + ); + assertEquals(0, LongStream.of(1, 2, 3, 4, 5).takeWhile(i -> i > 2).count()); + + assertEquals( + new long[] {0, 1, 2, 3, 4}, + LongStream.iterate(0, i -> i + 1).takeWhile(i -> i < 5).toArray() + ); + } + + public void testDropWhile() { + assertEquals( + new long[] {3, 4, 5}, + LongStream.of(1, 2, 3, 4, 5).dropWhile(i -> i < 3).toArray() + ); + assertEquals( + new long[] {1, 2, 3, 4, 5}, + LongStream.of(1, 2, 3, 4, 5).dropWhile(i -> i > 2).toArray() + ); + + // pass an infinite stream to dropWhile, ensure it handles it + assertEquals( + new long[] {5, 6, 7, 8, 9}, + LongStream.iterate(0, i -> i + 1).dropWhile(i -> i < 5).limit(5).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/emultest/java9/util/stream/StreamTest.java b/user/test/com/google/gwt/emultest/java9/util/stream/StreamTest.java new file mode 100644 index 00000000000..fb712ec7806 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java9/util/stream/StreamTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java9.util.stream; + +import com.google.gwt.emultest.java.util.EmulTestBase; + +import java.util.stream.Stream; + +/** + * Tests for java.util.stream.Stream Java 9 API emulation. + */ +public class StreamTest extends EmulTestBase { + public void testIterate() { + assertEquals( + new Integer[] {10, 11, 12, 13, 14}, + Stream.iterate(0, i -> i < 15, i -> i + 1).skip(10).toArray(Integer[]::new)); + + // Check that the functions are called the correct number of times with a hasNext limiting + // stream size + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + Integer[] array = Stream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).toArray(Integer[]::new); + + // Verify that the next was called for each value after the seed (plus one for testing that + // the stream is over) + assertEquals(array.length, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value (plus one as above) + assertEquals(array.length + 1, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new Integer[]{0, 1, 2, 3, 4}, array); + } + + // Same test repeated, but instead limit() will stop the stream from continuing + { + int[] nextCalledCount = {0}; + int[] hasNextCalledCount = {0}; + Integer[] array = Stream.iterate(0, val -> { + hasNextCalledCount[0]++; + return val < 5; + }, val -> { + nextCalledCount[0]++; + return val + 1; + }).limit(3).toArray(Integer[]::new); + + // Verify that the next was called for each value after the seed + assertEquals(array.length - 1, nextCalledCount[0]); + // Verify that the hasNext function was called once for each value + assertEquals(array.length, hasNextCalledCount[0]); + // Sanity check the values returned + assertEquals(new Integer[]{0, 1, 2}, array); + } + } + + public void testOfNullable() { + assertEquals(0, Stream.ofNullable(null).count()); + assertEquals( + new String[] {"abc"}, + Stream.ofNullable("abc").toArray(String[]::new) + ); + } + + public void testTakeWhile() { + assertEquals( + new Integer[] {1, 2}, + Stream.of(1, 2, 3, 4, 5).takeWhile(i -> i < 3).toArray(Integer[]::new) + ); + assertEquals(0, Stream.of(1, 2, 3, 4, 5).takeWhile(i -> i > 2).count()); + + assertEquals( + new Integer[] {0, 1, 2, 3, 4}, + Stream.iterate(0, i -> i + 1).takeWhile(i -> i < 5).toArray() + ); + } + + public void testDropWhile() { + assertEquals( + new Integer[] {3, 4, 5}, + Stream.of(1, 2, 3, 4, 5).dropWhile(i -> i < 3).toArray(Integer[]::new) + ); + assertEquals( + new Integer[] {1, 2, 3, 4, 5}, + Stream.of(1, 2, 3, 4, 5).dropWhile(i -> i > 2).toArray(Integer[]::new) + ); + + // pass an infinite stream to dropWhile, ensure it handles it + assertEquals( + new Integer[] {5, 6, 7, 8, 9}, + Stream.iterate(0, i -> i + 1).dropWhile(i -> i < 5).limit(5).toArray() + ); + } +} diff --git a/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java b/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java index 4bdf492b256..16eaf1b90d6 100644 --- a/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java +++ b/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import javax.servlet.ReadListener; import javax.servlet.ServletException; @@ -200,8 +201,8 @@ public void testGetCharsetInstances() { * default UTF-8 character set when passed a null encoding value. */ public void testGetDefaultCharset() { - assertEquals(Charset.forName("UTF-8"), RPCServletUtils.CHARSET_UTF8); - assertSame(RPCServletUtils.CHARSET_UTF8, RPCServletUtils.getCharset(null)); + assertEquals(Charset.forName("UTF-8"), StandardCharsets.UTF_8); + assertSame(StandardCharsets.UTF_8, RPCServletUtils.getCharset(null)); } /** diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionCompileTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionCompileTest.java index 25a54ef3b98..82df274e97a 100644 --- a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionCompileTest.java +++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionCompileTest.java @@ -46,7 +46,7 @@ public void testAllComposingConstraintsMustBeApplicableToAnnotatedType() Pattern.compile( "No \\@org\\.hibernate\\.jsr303\\.tck\\.tests\\.constraints\\.constraintcomposition" + "\\.NotEmpty\\(" - + "((\\s)*(message=\\{constraint\\.notEmpty\\}|payload=\\[\\]|groups=\\[\\])(,)?){3}" + + "((\\s)*(message=\"?\\{constraint\\.notEmpty\\}\"?|payload=(\\[\\]|\\{\\})|groups=(\\[\\]|\\{\\}))(,)?){3}" + "\\) ConstraintValidator for type int")); } diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorCompileTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorCompileTest.java index 8686d978d54..7dbed460fd8 100644 --- a/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorCompileTest.java +++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorCompileTest.java @@ -43,7 +43,7 @@ public void testUnexpectedTypeExceptionIsRaisedForInvalidType() UnexpectedTypeException.class, Pattern.compile( "No \\@org\\.hibernate\\.jsr303\\.tck\\.tests\\.constraints\\.customconstraint\\" + ".Positive\\(" - + "((\\s)*(message=\\{validation\\.positive\\}|payload=\\[\\]|groups=\\[\\])(,)?){3}" + + "((\\s)*(message=\"?\\{validation\\.positive\\}\"?|payload=(\\[\\]|\\{\\})|groups=(\\[\\]|\\{\\}))(,)?){3}" + "\\) ConstraintValidator for type class java\\.lang\\.String")); } diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionCompileTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionCompileTest.java index d5e61114441..7919a745e99 100644 --- a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionCompileTest.java +++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionCompileTest.java @@ -42,7 +42,7 @@ public void testAmbiguousValidatorResolution() Pattern.compile("More than one maximally specific " + "\\@org\\.hibernate\\.jsr303\\.tck\\.tests\\.constraints\\.validatorresolution" + ".Ambiguous\\(" - + "((\\s)*(message=foobar|payload=\\[\\]|groups=\\[\\])(,)?){3}" + + "((\\s)*(message=\"?foobar\"?|payload=(\\[\\]|\\{\\})|groups=(\\[\\]|\\{\\}))(,)?){3}" + "\\) " + "ConstraintValidator for type " + "class org\\.hibernate\\.jsr303\\.tck\\.tests\\.constraints" @@ -60,8 +60,8 @@ public void testUnexpectedTypeInValidatorResolution() assertBeanValidatorFailsToCompile(UnexpectedTypeValidator.class, Bar.class, ValidationException.class, Pattern.compile( "No \\@javax\\.validation\\.constraints\\.Size\\(" - + "((\\s)*(message=\\{javax\\.validation\\.constraints\\.Size\\.message\\}|" - + "min=0|max=2147483647|payload=\\[\\]|groups=\\[\\])(,)?){5}" + + "((\\s)*(message=\"?\\{javax\\.validation\\.constraints\\.Size\\.message\\}\"?|" + + "min=0|max=2147483647|payload=(\\[\\]|\\{\\})|groups=(\\[\\]|\\{\\}))(,)?){5}" + "\\) " + "ConstraintValidator for type class java\\.lang\\.Integer")); }