diff --git a/javadocs/readme.txt b/javadocs/readme.txt index cd819f5..0146fdb 100644 --- a/javadocs/readme.txt +++ b/javadocs/readme.txt @@ -1,11 +1,23 @@ -Put a ZIP file here named "java8.zip" containing Java's Javadocs (or where ever "BotConfig.JAVADOCS_DIR" points to) +This folder contains ZIP files generated by OakbotDoclet. -The ZIP file should contain the files that are in the "api" directory of the "Documentation" download, found here: -http://www.oracle.com/technetwork/java/javase/documentation/jdk8-doc-downloads-2133158.html +* Each ZIP file contains a collection of XML files, which contain parsed Javadoc information of a particular library. +* Each XML file contains the Javadoc information of a single class. +* The ZIP file also contains a "info.xml" file, which contains information about the library itself. -Make sure that a file called "allclasses-frames.html" is at the root of the ZIP. +======================= -I don't want to commit the real Javadoc file to version control, due to its large file size (~50MB), so I've committed a smaller, sample file instead. +To generate a ZIP file: + +1. Clone and open the OakBot project: http://github.com/mangstadt/OakBot +2. Compile the OakbotDoclet class. You will need to include the JVM's "tools.jar" file on the classpath during compilation. This file can be found at "$JAVA_HOME/lib/tools.jar" +3. Tweak the "build-zip.sh" file as necessary and then execute it. Note that you'll need a copy of the library's source code on your computer. + +======================= + +I don't want to commit the ZIP for the Java 8 API to version control, due to its large file size (~6MB), so I've committed a smaller, sample file instead. The sample ZIP file contains just a single class (java.lang.String). +ZIP files for various libraries (including the core Java 8 API) can be downloaded here: +https://www.dropbox.com/sh/xkf7kua3hzd8xvo/AAC1sOkVTNUE2MKPAXTm28bna?dl=0 + -Michael \ No newline at end of file diff --git a/javadocs/sample.zip b/javadocs/sample.zip index 87d0b62..b010864 100644 Binary files a/javadocs/sample.zip and b/javadocs/sample.zip differ diff --git a/src/main/java/com/gmail/inverseconduit/bot/DefaultBot.java b/src/main/java/com/gmail/inverseconduit/bot/DefaultBot.java index 9017f74..ab374dd 100644 --- a/src/main/java/com/gmail/inverseconduit/bot/DefaultBot.java +++ b/src/main/java/com/gmail/inverseconduit/bot/DefaultBot.java @@ -34,6 +34,7 @@ public class DefaultBot extends AbstractBot implements Subscribable commands = new HashSet<>(); + protected final Set listeners = new HashSet<>(); public DefaultBot(ChatInterface chatInterface) { this.chatInterface = chatInterface; @@ -52,6 +53,8 @@ private void processMessageQueue() { } private void processMessage(final ChatMessage chatMessage) { + listeners.stream().map(l -> l.execute(chatMessage)).filter(l -> null != l).forEach(result -> chatInterface.sendMessage(SeChatDescriptor.buildSeChatDescriptorFrom(chatMessage), result)); + final String trigger = AppContext.INSTANCE.get(BotConfig.class).getTrigger(); if ( !chatMessage.getMessage().startsWith(trigger)) { return; } @@ -62,14 +65,23 @@ private void processMessage(final ChatMessage chatMessage) { public Set getCommands() { return Collections.unmodifiableSet(commands); } + + public Set getListeners(){ + return Collections.unmodifiableSet(listeners); + } @Override public void subscribe(CommandHandle subscriber) { - commands.add(subscriber); + if (subscriber.getName() == null){ + listeners.add(subscriber); + } else { + commands.add(subscriber); + } } @Override public void unSubscribe(CommandHandle subscriber) { + listeners.remove(subscriber); commands.remove(subscriber); } @@ -79,7 +91,9 @@ public void shutdown() { @Override public Collection getSubscriptions() { - return Collections.unmodifiableCollection(commands); + Set set = new HashSet(commands); + set.addAll(listeners); + return set; } } diff --git a/src/main/java/com/gmail/inverseconduit/bot/Program.java b/src/main/java/com/gmail/inverseconduit/bot/Program.java index bf88037..e6a695e 100644 --- a/src/main/java/com/gmail/inverseconduit/bot/Program.java +++ b/src/main/java/com/gmail/inverseconduit/bot/Program.java @@ -102,15 +102,30 @@ private void login() { private void bindDefaultCommands() { bindShutdownCommand(); + bindNumberCommand(); bindJavaDocCommand(); new CoreBotCommands(chatInterface, bot).allCommands().forEach(bot::subscribe); } + + private void bindNumberCommand() { + final Pattern p = Pattern.compile("^\\d+$"); + CommandHandle javaDoc = new CommandHandle.Builder(null, message -> { + Matcher matcher = p.matcher(message.getMessage()); + if (!matcher.find()){ + return null; + } + + int choice = Integer.parseInt(matcher.group(0)); + return javaDocAccessor.showChoice(message, choice); + }).build(); + bot.subscribe(javaDoc); + } private void bindJavaDocCommand() { CommandHandle javaDoc = new CommandHandle.Builder("javadoc", message -> { Matcher matcher = javadocPattern.matcher(message.getMessage()); matcher.find(); - return javaDocAccessor.javadoc(message, matcher.group(1).trim()); + return javaDocAccessor.javadoc(message, matcher.group(1)); }).build(); bot.subscribe(javaDoc); } diff --git a/src/main/java/com/gmail/inverseconduit/commands/CommandHandle.java b/src/main/java/com/gmail/inverseconduit/commands/CommandHandle.java index 05db77c..5acd815 100644 --- a/src/main/java/com/gmail/inverseconduit/commands/CommandHandle.java +++ b/src/main/java/com/gmail/inverseconduit/commands/CommandHandle.java @@ -107,7 +107,10 @@ public String execute(ChatMessage message) { @Deprecated public boolean matchesSyntax(String commandCall) { // FIXME: Interimsimplementation. To be removed! - return commandCall.contains(name); + if (commandCall != null && name != null){ + return commandCall.contains(name); + } + return false; } public String getHelpText() { diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfo.java b/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfo.java index 6f20c9b..baf8812 100644 --- a/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfo.java +++ b/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfo.java @@ -1,63 +1,187 @@ package com.gmail.inverseconduit.javadoc; +import java.util.Collection; import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; /** * Holds the Javadoc info of a class. * @author Michael Angstadt */ public class ClassInfo { - private final String fullName; + private final ClassName name; + private final ClassName superClass; private final String description; - private final String url; - private final List modifiers; + private final Set modifiers; + private final Set interfaces; + private final Multimap methods; private final boolean deprecated; + private final LibraryZipFile zipFile; + + private ClassInfo(Builder builder) { + name = builder.name; + superClass = builder.superClass; + description = builder.description; + modifiers = builder.modifiers.build(); + interfaces = builder.interfaces.build(); + methods = builder.methods.build(); + deprecated = builder.deprecated; + zipFile = builder.zipFile; + } - public ClassInfo(String fullName, String description, String url, List modifiers, boolean deprecated) { - this.fullName = fullName; - this.description = description; - this.url = url; - this.modifiers = modifiers; - this.deprecated = deprecated; + /** + * Gets the name of the class. + * @return the class name + */ + public ClassName getName() { + return name; } /** - * Gets the class's fully-qualified name. - * @return the fully-qualified name (e.g. "java.lang.String") + * Gets the name of the class's parent class. + * @return the parent class name or null if it doesn't have one */ - public String getFullName() { - return fullName; + public ClassName getSuperClass() { + return superClass; } /** - * Gets the class's description. - * @return the class description, formatted in SO Chat's markdown language + * Gets the class description. + * @return the class description (in SO-Chat markdown) */ public String getDescription() { return description; } /** - * Gets the URL where this class's Javadocs can be viewed online. - * @return the URL or null if unknown + * Gets the URL to this class's Javadoc page (with frames). + * @return the URL or null if no base URL was given + */ + public String getFrameUrl() { + return zipFile.getFrameUrl(this); + } + + /** + * Gets the URL to this class's Javadoc page (without frames). + * @return the URL or null if no base URL was given */ public String getUrl() { - return url; + return zipFile.getUrl(this); } /** - * Gets the modifiers of this class. - * @return the modifiers (e.g. "public", "final", "class") + * Gets the class modifiers. + * @return the class modifiers (e.g. "public, final") */ - public List getModifiers() { + public Collection getModifiers() { return modifiers; } /** - * Gets whether the class is deprecated or not. + * Gets the names of the interfaces that the class implements. + * @return the class's interfaces + */ + public Collection getInterfaces() { + return interfaces; + } + + /** + * Gets info on all of the methods that have the given name. + * @param name the method name (case insensitive) + * @return the methods + */ + public Collection getMethod(String name) { + return methods.get(name.toLowerCase()); + } + + /** + * Gets the class's methods. + * @return the class's methods + */ + public Collection getMethods() { + return methods.values(); + } + + /** + * Determines whether the class is deprecated or not. * @return true if it's deprecated, false if not */ public boolean isDeprecated() { return deprecated; } + + /** + * Gets the ZIP file that the class's parsed Javadoc info is stored in. + * @return the ZIP file + */ + public LibraryZipFile getZipFile() { + return zipFile; + } + + /** + * Builds new instances of {@link ClassInfo}. + */ + public static class Builder { + private ClassName name; + private ClassName superClass; + private String description; + private ImmutableSet.Builder modifiers = ImmutableSet.builder(); + private ImmutableSet.Builder interfaces = ImmutableSet.builder(); + private ImmutableMultimap.Builder methods = ImmutableMultimap.builder(); + private boolean deprecated = false; + private LibraryZipFile zipFile; + + public Builder name(String full, String simple) { + this.name = new ClassName(full, simple); + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder superClass(String superClass) { + this.superClass = new ClassName(superClass); + return this; + } + + public Builder modifiers(List modifiers) { + this.modifiers.addAll(modifiers); + return this; + } + + public Builder interface_(String interface_) { + interfaces.add(new ClassName(interface_)); + return this; + } + + public Builder interfaces(List interfaces) { + this.interfaces.addAll(interfaces); + return this; + } + + public Builder method(MethodInfo method) { + this.methods.put(method.getName().toLowerCase(), method); + return this; + } + + public Builder deprecated(boolean deprecated) { + this.deprecated = deprecated; + return this; + } + + public Builder zipFile(LibraryZipFile zipFile) { + this.zipFile = zipFile; + return this; + } + + public ClassInfo build() { + return new ClassInfo(this); + } + } } diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfoXmlParser.java b/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfoXmlParser.java new file mode 100644 index 0000000..7c4771b --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/javadoc/ClassInfoXmlParser.java @@ -0,0 +1,179 @@ +package com.gmail.inverseconduit.javadoc; + +import java.util.Arrays; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.gmail.inverseconduit.utils.DocumentWrapper; + +/** + * Parses {@link ClassInfo} objects from XML documents. + * @author Michael Angstadt + */ +public class ClassInfoXmlParser { + private final DocumentWrapper document; + private final LibraryZipFile zipFile; + + /** + * @param document the XML document to parse + * @param zipFile the ZIP file the class belongs to + */ + public ClassInfoXmlParser(Document document, LibraryZipFile zipFile) { + this.document = new DocumentWrapper(document); + this.zipFile = zipFile; + } + + /** + * Parses the {@link ClassInfo} object out of the XML data. + * @return the parse object + */ + public ClassInfo parse() { + ClassInfo.Builder builder = new ClassInfo.Builder(); + builder.zipFile(zipFile); + + //class name + Element classElement = document.element("/class"); + String fullName = classElement.getAttribute("fullName"); + String simpleName = classElement.getAttribute("simpleName"); + builder.name(fullName, simpleName); + + //modifiers + String value = classElement.getAttribute("modifiers"); + if (!value.isEmpty()) { + builder.modifiers(Arrays.asList(value.split("\\s+"))); + } + + //super class + value = classElement.getAttribute("extends"); + if (!value.isEmpty()) { + builder.superClass(value); + } + + //interfaces + value = classElement.getAttribute("implements"); + if (!value.isEmpty()) { + for (String full : Arrays.asList(value.split("\\s+"))) { + builder.interface_(full); + } + } + + //deprecated + value = classElement.getAttribute("deprecated"); + builder.deprecated(value.isEmpty() ? false : Boolean.parseBoolean(value)); + + //description + Element element = document.element("/class/description"); + if (element != null) { + builder.description(element.getTextContent()); + } + + //constructors + for (Element constructorElement : document.elements("/class/constructors/constructor")) { + MethodInfo info = parseConstructor(constructorElement, simpleName); + builder.method(info); + } + + //methods + for (Element methodElement : document.elements("/class/methods/method")) { + MethodInfo method = parseMethod(methodElement); + builder.method(method); + } + + return builder.build(); + } + + private MethodInfo parseConstructor(Element element, String simpleName) { + MethodInfo.Builder builder = new MethodInfo.Builder(); + + //name + builder.name(simpleName); + + //modifiers + String value = element.getAttribute("modifiers"); + if (!value.isEmpty()) { + builder.modifiers(Arrays.asList(value.split("\\s+"))); + } + + //description + Element descriptionElement = document.element("description", element); + if (descriptionElement != null) { + builder.description(descriptionElement.getTextContent()); + } + + //deprecated + value = element.getAttribute("deprecated"); + builder.deprecated(value.isEmpty() ? false : Boolean.parseBoolean(value)); + + //parameters + for (Element parameterElement : document.elements("parameters/parameter", element)) { + ParameterInfo parameter = parseParameter(parameterElement); + builder.parameter(parameter); + } + + return builder.build(); + } + + private MethodInfo parseMethod(Element element) { + MethodInfo.Builder builder = new MethodInfo.Builder(); + + //name + builder.name(element.getAttribute("name")); + + //modifiers + String value = element.getAttribute("modifiers"); + if (!value.isEmpty()) { + builder.modifiers(Arrays.asList(value.split("\\s+"))); + } + + //description + Element descriptionElement = document.element("description", element); + if (descriptionElement != null) { + builder.description(descriptionElement.getTextContent()); + } + + //return value + value = element.getAttribute("returns"); + if (!value.isEmpty()) { + builder.returnValue(new ClassName(value)); + } + + //deprecated + value = element.getAttribute("deprecated"); + builder.deprecated(value.isEmpty() ? false : Boolean.parseBoolean(value)); + + //parameters + for (Element parameterElement : document.elements("parameters/parameter", element)) { + ParameterInfo parameter = parseParameter(parameterElement); + builder.parameter(parameter); + } + + return builder.build(); + } + + private ParameterInfo parseParameter(Element element) { + String type = element.getAttribute("type"); + + //is it an array? + boolean array = type.endsWith("[]"); + if (array) { + type = type.substring(0, type.length() - 2); + } + boolean varargs = type.endsWith("..."); + if (varargs) { + type = type.substring(0, type.length() - 3); + } + + //is a generic type? (like List) + int pos = type.indexOf('<'); + String generic = (pos < 0) ? null : type.substring(pos); + if (generic != null) { + type = type.substring(0, pos); + } + + //name + String name = element.getAttribute("name"); + + return new ParameterInfo(new ClassName(type), name, array, varargs, generic); + } +} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/ClassName.java b/src/main/java/com/gmail/inverseconduit/javadoc/ClassName.java new file mode 100644 index 0000000..0d2d2ef --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/javadoc/ClassName.java @@ -0,0 +1,52 @@ +package com.gmail.inverseconduit.javadoc; + +/** + * Represents the name of a class. + * @author Michael Angstadt + */ +public class ClassName { + private final String fullyQualified; + private final String simple; + + /** + * @param fullyQualified the fully-qualified class name (e.g. + * "java.lang.String") + */ + public ClassName(String fullyQualified) { + this.fullyQualified = fullyQualified; + + int pos = fullyQualified.lastIndexOf('.'); + simple = (pos < 0) ? fullyQualified : fullyQualified.substring(pos + 1); + } + + /** + * @param fullyQualified the fully-qualified class name (e.g. + * "java.lang.String") + * @param simple the simple class name (e.g. "String") + */ + public ClassName(String fullyQualified, String simple) { + this.fullyQualified = fullyQualified; + this.simple = simple; + } + + /** + * Gets the fully-qualified class name + * @return the fully-qualified class name (e.g. "java.lang.String") + */ + public String getFullyQualified() { + return fullyQualified; + } + + /** + * Gets the simple class name. + * @return the simple class name (e.g. "String") + */ + public String getSimple() { + return simple; + } + + @Override + public String toString() { + return fullyQualified; + } +} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/DescriptionNodeVisitor.java b/src/main/java/com/gmail/inverseconduit/javadoc/DescriptionNodeVisitor.java deleted file mode 100644 index a27cdad..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/DescriptionNodeVisitor.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.util.regex.Pattern; - -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; -import org.jsoup.nodes.TextNode; -import org.jsoup.select.NodeVisitor; - -/** - * Iterates through the description section of a class's Javadoc HTML page, - * converting the description to SO Chat markdown. - * @author Michael Angstadt - */ -public class DescriptionNodeVisitor implements NodeVisitor { - private final StringBuilder sb = new StringBuilder(); - private final Pattern escapeRegex = Pattern.compile("[*_\\[\\]]"); - private boolean inPre = false; - private String prevText; - private String linkUrl, linkTitle, linkText; - - @Override - public void head(Node node, int depth) { - //for (int i = 0; i < depth; i++) { - // System.out.print(' '); - //} - //System.out.println("head " + node.nodeName()); - //if (node instanceof TextNode) { - // for (int i = 0; i < depth; i++) { - // System.out.print(' '); - // } - // System.out.println(((TextNode) node).text()); - //} - - switch (node.nodeName()) { - case "a": - Element element = (Element) node; - String href = element.absUrl("href"); - if (!href.isEmpty()) { - linkUrl = href; - linkTitle = element.attr("title"); - } - break; - case "code": - case "tt": - sb.append("`"); - break; - case "i": - case "em": - sb.append("*"); - break; - case "b": - case "strong": - sb.append("**"); - break; - case "br": - case "p": - sb.append("\n"); - break; - case "pre": - inPre = true; - sb.append("\n"); - break; - case "#text": - TextNode text = (TextNode) node; - String content; - if (inPre) { - content = text.getWholeText(); - } else { - content = text.text(); - content = escapeRegex.matcher(content).replaceAll("\\\\$0"); //escape special chars - } - - //in the jsoup javadocs, it's reading some text nodes twice for some reason - //so, ignore the duplicate text nodes - if (prevText != null && prevText.equals(content)) { - prevText = null; - break; - } - prevText = content; - - if (inLink()) { - linkText = content; - } else { - sb.append(content); - } - - break; - } - } - - @Override - public void tail(Node node, int depth) { - //for (int i = 0; i < depth; i++) { - // System.out.print(' '); - //} - //System.out.println("tail " + node.nodeName()); - - switch (node.nodeName()) { - case "a": - if (inLink()) { - sb.append("[").append(linkText).append("](").append(linkUrl); - if (!linkTitle.isEmpty()) { - sb.append(" \"").append(linkTitle).append("\""); - } - sb.append(")"); - - linkUrl = linkText = linkTitle = null; - } - break; - case "code": - case "tt": - sb.append("`"); - break; - case "i": - case "em": - sb.append("*"); - break; - case "b": - case "strong": - sb.append("**"); - break; - case "p": - sb.append("\n"); - break; - case "pre": - inPre = false; - sb.append("\n"); - break; - } - } - - private boolean inLink() { - return linkUrl != null; - } - - /** - * Gets the {@link StringBuilder} used to hold the description. - * @return the string builder - */ - public StringBuilder getStringBuilder() { - return sb; - } -} \ No newline at end of file diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/Java8PageParser.java b/src/main/java/com/gmail/inverseconduit/javadoc/Java8PageParser.java deleted file mode 100644 index 098f17f..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/Java8PageParser.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.TextNode; - -/** - * Parses the Java 8 Javadocs. - * @author Michael Angstadt - */ -public class Java8PageParser implements PageParser { - @Override - public List parseClassNames(Document document) { - List classNames = new ArrayList<>(); - for (Element element : document.select("ul li a")) { - String url = element.attr("href"); - int dotPos = url.lastIndexOf('.'); - if (dotPos < 0) { - continue; - } - - url = url.substring(0, dotPos); - url = url.replace('/', '.'); - classNames.add(url); - } - return classNames; - } - - @Override - public ClassInfo parseClassPage(Document document, String className) { - String description; - { - Element descriptionElement = document.select(".block").first(); - DescriptionNodeVisitor visitor = new DescriptionNodeVisitor(); - descriptionElement.traverse(visitor); - description = visitor.getStringBuilder().toString().trim(); - } - - String url = getBaseUrl() + "?" + className.replace('.', '/') + ".html"; - - boolean deprecated = false; - List modifiers; - { - Element element = document.select(".typeNameLabel").first(); - if (element == null) { - //it might be an annotation - element = document.select(".memberNameLabel").first(); - } - TextNode textNode = (TextNode) element.siblingNodes().get(element.siblingIndex() - 1); - - //sometimes, other text comes before the modifiers on the previous line - String text = textNode.getWholeText(); - int pos = text.lastIndexOf('\n'); - if (pos >= 0) { - text = text.substring(pos + 1); - } - - modifiers = Arrays.asList(text.trim().split(" ")); - - //look for @Deprecated annotation - Element parent = (Element) textNode.parent(); - for (Element child : parent.children()) { - if ("@Deprecated".equals(child.text())) { - deprecated = true; - break; - } - } - } - - return new ClassInfo(className, description, url, modifiers, deprecated); - } - - @Override - public String getBaseUrl() { - return "https://docs.oracle.com/javase/8/docs/api/"; - } -} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/JavaDocAccessor.java b/src/main/java/com/gmail/inverseconduit/javadoc/JavaDocAccessor.java index d1e0c62..6050e1f 100644 --- a/src/main/java/com/gmail/inverseconduit/javadoc/JavaDocAccessor.java +++ b/src/main/java/com/gmail/inverseconduit/javadoc/JavaDocAccessor.java @@ -1,130 +1,681 @@ package com.gmail.inverseconduit.javadoc; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.gmail.inverseconduit.datatype.ChatMessage; +import com.gmail.inverseconduit.utils.ChatBuilder; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +/** + * Generates chat message responses to the "javadoc" command. Also generates + * responses to numeric chat messages that users enter when the javadoc command + * presents them with a list of choices. + */ public class JavaDocAccessor { + /** + * Used for accessing the Javadoc information. + */ + private final JavadocDao dao; + /** + * The most recent list of suggestions that were sent to the chat. + */ + private List prevChoices = new ArrayList<>(); - private final JavadocDao dao; - - /** - * @param chatInterface - * interface to the chat - * @param dir - * the directory to the Javadocs folder - * @throws IOException - * if there's a problem reading a Javadoc file - */ - public JavaDocAccessor(Path dir) throws IOException { - dao = new JavadocDao(); - - Path java8Api = dir.resolve("java8.zip"); - if (Files.exists(java8Api)) { - PageLoader loader = new ZipPageLoader(java8Api); - PageParser parser = new Java8PageParser(); - dao.addJavadocApi(loader, parser); - } - else { - //for testing purposes - //this ZIP only has the "java.lang.String" class - Path sample = dir.resolve("sample.zip"); - if (Files.exists(sample)) { - PageLoader loader = new ZipPageLoader(sample); - PageParser parser = new Java8PageParser(); - dao.addJavadocApi(loader, parser); - } - } - } - - public String javadoc(ChatMessage chatMessage, String commandText) { - String response; - try { - response = generateResponse(commandText); - } catch(IOException e) { - throw new RuntimeException("Problem getting Javadoc info.", e); - } - - return ":" + chatMessage.getMessageId() + " " + response; - } - - private String generateResponse(String commandText) throws IOException { - ClassInfo info; - try { - info = dao.getClassInfo(commandText); - } catch(MultipleClassesFoundException e) { - StringBuilder sb = new StringBuilder(); - sb.append("Which one do you mean?"); - for (String name : e.getClasses()) { - sb.append("\n* ").append(name); - } - return sb.toString(); - } - - if (info == null) { return "Sorry, I never heard of that class. :("; } - - StringBuilder sb = new StringBuilder(); - - boolean deprecated = info.isDeprecated(); - for (String modifier : info.getModifiers()) { - boolean italic = false; - switch (modifier) { - case "abstract": - case "final": - italic = true; - break; - case "class": - case "enum": - case "interface": - italic = false; - break; - case "@interface": - italic = false; - modifier = "annotation"; - break; - default: - //ignore all the rest - continue; - } - - if (italic) - sb.append('*'); - if (deprecated) - sb.append("---"); - sb.append("[tag:").append(modifier).append("]"); - if (deprecated) - sb.append("---"); - if (italic) - sb.append('*'); - sb.append(' '); - } - - if (deprecated) - sb.append("---"); - String fullName = info.getFullName(); - String url = info.getUrl(); - if (url == null) { - sb.append("**`").append(fullName).append("`**"); - } - else { - sb.append("[**`").append(fullName).append("`**](").append(url).append(" \"View the Javadocs\")"); - } - if (deprecated) - sb.append("---"); - sb.append(": "); - - //get the class description - String description = info.getDescription(); - int pos = description.indexOf("\n"); - if (pos >= 0) { - //just display the first paragraph - description = description.substring(0, pos); - } - sb.append(description); - - return sb.toString(); - } + /** + * The last time the list of previous choices were accessed in some way + * (timestamp). + */ + private long prevChoicesPinged = 0; + + /** + * Stop responding to numeric choices the user enters after this amount of + * time. + */ + private static final long choiceTimeout = TimeUnit.SECONDS.toMillis(30); + + /** + * "Flags" that a class can have. They are defined in a List because, if a + * class has multiple modifiers, I want them to be displayed in a consistent + * order. + */ + private static final List classModifiers; + static { + ImmutableList.Builder b = new ImmutableList.Builder<>(); + b.add("abstract", "final"); + classModifiers = b.build(); + } + + /** + * The list of possible "class types". Each class *should* have exactly one + * type, but there's no explicit check for this (things shouldn't break if a + * class does not have exactly one). + */ + private static final Set classTypes; + static { + ImmutableSet.Builder b = new ImmutableSet.Builder<>(); + b.add("annotation", "class", "enum", "exception", "interface"); + classTypes = b.build(); + } + + /** + * The method modifiers to ignore when outputting a method to the chat. + */ + private static final Set methodModifiersToIgnore; + static { + ImmutableSet.Builder b = new ImmutableSet.Builder<>(); + b.add("private", "protected", "public"); + methodModifiersToIgnore = b.build(); + } + + /** + * @param dir the directory to the Javadocs folder + * @throws IOException if there's a problem reading a Javadoc file + */ + public JavaDocAccessor(Path dir) throws IOException { + dao = new JavadocDao(dir); + } + + /** + * Processes a Javadoc command. + * @param chatMessage the chat message (e.g. "**javadoc: arraylist") + * @param commandText the command text (e.g. "arraylist") + * @return the chat response + */ + public String javadoc(ChatMessage chatMessage, String commandText) { + String response = generateResponse(commandText.trim()); + + ChatBuilder cb = new ChatBuilder(); + cb.reply(chatMessage); + cb.append(response); + return cb.toString(); + } + + private String generateResponse(String commandTextStr) { + if (commandTextStr.isEmpty()) { + return "Type the name of a Java class (e.g. \"java.lang.String\") or a method (e.g. \"Integer#parseInt\")."; + } + + //parse the command + CommandTextParser commandText = new CommandTextParser(commandTextStr); + + ClassInfo info; + try { + info = dao.getClassInfo(commandText.className); + } catch (IOException e) { + throw new RuntimeException("Problem getting Javadoc info.", e); + } catch (MultipleClassesFoundException e) { + return handleMultipleMatches(commandText, e.getClasses()); + } + + if (info == null) { + //couldn't find the class + return "Sorry, I never heard of that class. :("; + } + + return handleSingleMatch(commandText, info); + } + + /** + * Generates the chat response for when the user's query returns a single + * class. + * @param commandText the command text + * @param info the class that was found + * @return the chat response + */ + private String handleSingleMatch(CommandTextParser commandText, ClassInfo info) { + if (commandText.methodName == null) { + //method name not specified, so print the class docs + return printClass(info, commandText.paragraph); + } + + //print the method docs + MatchingMethods matchingMethods; + try { + matchingMethods = getMatchingMethods(info, commandText.methodName, commandText.parameters); + } catch (IOException e) { + throw new RuntimeException("Problem getting Javadoc info.", e); + } + + if (matchingMethods.isEmpty()) { + //no matches found + return "Sorry, I can't find that method. :("; + } + + if (matchingMethods.exactSignature != null) { + //an exact match was found! + return printMethod(matchingMethods.exactSignature, info, commandText.paragraph); + } + + if (matchingMethods.matchingName.size() == 1 && commandText.parameters == null) { + return printMethod(matchingMethods.matchingName.get(0), info, commandText.paragraph); + } + + //print the methods with the same name + Multimap map = ArrayListMultimap.create(); + map.putAll(info, matchingMethods.matchingName); + return printMethodChoices(map, commandText.parameters); + } + + /** + * Generates the chat response for when the user's query returns more than + * one class. + * @param commandText the command text + * @param matches the fully-qualified names of the classes that were found + * @return the chat response + */ + private String handleMultipleMatches(CommandTextParser commandText, Collection matches) { + if (commandText.methodName == null) { + //just print the class choices, since the user did not specify a method + return printClassChoices(matches); + } + + //search each class for a method that matches the given signature + Map exactMatches = new HashMap<>(); + Multimap matchingNames = ArrayListMultimap.create(); + for (String className : matches) { + ClassInfo classInfo; + MatchingMethods methods; + try { + classInfo = dao.getClassInfo(className); + methods = getMatchingMethods(classInfo, commandText.methodName, commandText.parameters); + } catch (IOException e) { + throw new RuntimeException("Problem getting Javadoc info.", e); + } + + if (methods.exactSignature != null) { + exactMatches.put(classInfo, methods.exactSignature); + } + matchingNames.putAll(classInfo, methods.matchingName); + } + + if (exactMatches.isEmpty() && matchingNames.isEmpty()) { + //no matches found + return "Sorry, I can't find that method. :("; + } + + if (exactMatches.size() == 1) { + //a single, exact match was found! + MethodInfo method = exactMatches.values().iterator().next(); + ClassInfo classInfo = exactMatches.keySet().iterator().next(); + return printMethod(method, classInfo, commandText.paragraph); + } + + //multiple matches were found + Multimap choicesToPrint; + if (exactMatches.size() > 1) { + choicesToPrint = ArrayListMultimap.create(); + for (Map.Entry entry : exactMatches.entrySet()) { + choicesToPrint.put(entry.getKey(), entry.getValue()); + } + } else { + choicesToPrint = matchingNames; + } + return printMethodChoices(choicesToPrint, commandText.parameters); + } + + /** + * Called when someone types a number into the chat, in response to the + * javadoc command showing a list of choices. + * @param message the chat message + * @param num the number + * @return the chat response or null to ignore the message + */ + public String showChoice(ChatMessage message, int num) { + if (prevChoicesPinged == 0) { + return null; + } + + boolean timedOut = System.currentTimeMillis() - prevChoicesPinged > choiceTimeout; + if (timedOut) { + return null; + } + + //reset the time-out timer + prevChoicesPinged = System.currentTimeMillis(); + + int index = num - 1; + if (index < 0 || index >= prevChoices.size()) { + ChatBuilder cb = new ChatBuilder(); + cb.reply(message).append("That's not a valid choice."); + return cb.toString(); + + } + + String msg = prevChoices.get(index); + return javadoc(message, msg); + } + + /** + * Prints the Javadoc info of a particular method. + * @param methodinfo the method + * @param classInfo the class that the method belongs to + * @param paragraph the paragraph to print + * @return the chat response + */ + private String printMethod(MethodInfo methodInfo, ClassInfo classInfo, int paragraph) { + ChatBuilder cb = new ChatBuilder(); + if (paragraph == 1) { + //print library name + LibraryZipFile zipFile = classInfo.getZipFile(); + if (zipFile != null) { + String name = zipFile.getName(); + if (name != null && !name.equalsIgnoreCase("java")) { + name = name.replace(' ', '-'); + cb.bold(); + cb.tag(name); + cb.bold(); + cb.append(' '); + } + } + + //print modifiers + boolean deprecated = methodInfo.isDeprecated(); + Collection modifiersToPrint = new ArrayList(methodInfo.getModifiers()); + modifiersToPrint.removeAll(methodModifiersToIgnore); + for (String modifier : modifiersToPrint) { + if (deprecated) cb.strike(); + cb.tag(modifier); + if (deprecated) cb.strike(); + cb.append(' '); + } + + //print signature + if (deprecated) cb.strike(); + String signature = methodInfo.getSignatureString(); + String url = classInfo.getUrl(); + if (url == null) { + cb.bold().code(signature).bold(); + } else { + url += "#" + methodInfo.getUrlAnchor(); + cb.link(new ChatBuilder().bold().code(signature).bold().toString(), url); + } + if (deprecated) cb.strike(); + cb.append(": "); + } + + //print the method description + String description = methodInfo.getDescription(); + Paragraphs paragraphs = new Paragraphs(description); + paragraphs.append(paragraph, cb); + return cb.toString(); + } + + /** + * Prints the methods to choose from when multiple methods are found. + * @param matchingMethods the methods to choose from + * @param methodParams the parameters of the method or null if no parameters + * were specified + * @return the chat response + */ + private String printMethodChoices(Multimap matchingMethods, List methodParams) { + prevChoices = new ArrayList<>(); + prevChoicesPinged = System.currentTimeMillis(); + ChatBuilder cb = new ChatBuilder(); + cb.append(buildMethodChoiceQuestion(matchingMethods, methodParams)); + + int count = 1; + for (Map.Entry entry : matchingMethods.entries()) { + ClassInfo classInfo = entry.getKey(); + MethodInfo methodInfo = entry.getValue(); + + String signature; + { + StringBuilder sb = new StringBuilder(); + sb.append(classInfo.getName().getFullyQualified()).append("#").append(methodInfo.getName()); + + List paramList = new ArrayList<>(); + for (ParameterInfo param : methodInfo.getParameters()) { + paramList.add(param.getType().getSimple() + (param.isArray() ? "[]" : "")); + } + sb.append('(').append(String.join(", ", paramList)).append(')'); + + signature = sb.toString(); + } + + cb.nl().append(count + "").append(". ").append(signature); + prevChoices.add(signature); + count++; + } + return cb.toString(); + } + + private String buildMethodChoiceQuestion(Multimap matchingMethods, List methodParams) { + if (matchingMethods.size() == 1) { + if (methodParams == null) { + return "Did you mean this one? (type the number)"; + } + + if (methodParams.isEmpty()) { + return "I couldn't find a zero-arg signature for that method."; + } + + //@formatter:off + return + "I couldn't find a signature with " + + ((methodParams.size() == 1) ? "that parameter." : "those parameters.") + + " Did you mean this one? (type the number)"; + //@formatter:on + } + + if (methodParams == null) { + return "Which one do you mean? (type the number)"; + } + + StringBuilder sb = new StringBuilder(); + if (methodParams.isEmpty()) { + sb.append("I couldn't find a zero-arg signature for that method."); + } else { + sb.append("I couldn't find a signature with "); + sb.append((methodParams.size() == 1) ? "that parameter." : "those parameters."); + } + sb.append(" Did you mean one of these? (type the number)"); + return sb.toString(); + } + + /** + * Prints the classes to choose from when multiple class are found. + * @param classes the fully-qualified names of the classes + * @return the chat response + */ + private String printClassChoices(Collection classes) { + List choices = new ArrayList<>(classes); + Collections.sort(choices); + prevChoices = choices; + prevChoicesPinged = System.currentTimeMillis(); + + ChatBuilder cb = new ChatBuilder(); + cb.append("Which one do you mean? (type the number)"); + + int count = 1; + for (String name : choices) { + cb.nl().append(count + "").append(". ").append(name); + count++; + } + + return cb.toString(); + } + + /** + * Prints the description of a class. + * @param info the class info + * @param paragraph the paragraph to print + * @return the chat response + */ + private String printClass(ClassInfo info, int paragraph) { + ChatBuilder cb = new ChatBuilder(); + if (paragraph == 1) { + //print the library name + LibraryZipFile zipFile = info.getZipFile(); + if (zipFile != null) { + String name = zipFile.getName(); + if (name != null && !name.equalsIgnoreCase("Java")) { + name = name.replace(" ", "-"); + cb.bold(); + cb.tag(name); + cb.bold(); + cb.append(' '); + } + } + + //print modifiers + boolean deprecated = info.isDeprecated(); + Collection infoModifiers = info.getModifiers(); + List modifiersToPrint = new ArrayList<>(classModifiers); + modifiersToPrint.retainAll(infoModifiers); + + //add class modifiers + for (String classModifier : modifiersToPrint) { + cb.italic(); + if (deprecated) cb.strike(); + cb.tag(classModifier); + if (deprecated) cb.strike(); + cb.italic(); + cb.append(' '); + } + + Collection classType = new HashSet<>(classTypes); + classType.retainAll(infoModifiers); + //there should be only one remaining element in the collection, but use a foreach loop just incase + for (String modifier : classType) { + if (deprecated) cb.strike(); + cb.tag(modifier); + if (deprecated) cb.strike(); + cb.append(' '); + } + + //print class name + if (deprecated) cb.strike(); + String fullName = info.getName().getFullyQualified(); + String url = info.getFrameUrl(); + if (url == null) { + cb.bold().code(fullName).bold(); + } else { + cb.link(new ChatBuilder().bold().code(fullName).bold().toString(), url); + } + if (deprecated) cb.strike(); + cb.append(": "); + } + + //print the class description + String description = info.getDescription(); + Paragraphs paragraphs = new Paragraphs(description); + paragraphs.append(paragraph, cb); + return cb.toString(); + } + + /** + * Finds the methods in a given class that matches the given method + * signature. + * @param info the class to search + * @param methodName the name of the method to search for + * @param methodParameters the parameters that the method should have, or + * null not to look at the parameters. + * @return the matching methods + * @throws IOException if there's a problem loading Javadoc info + */ + private MatchingMethods getMatchingMethods(ClassInfo info, String methodName, List methodParameters) throws IOException { + MatchingMethods matchingMethods = new MatchingMethods(); + Set matchingNameSignatures = new HashSet<>(); + + //search the class, all its parent classes, and all its interfaces and the interfaces of its super classes + LinkedList typeStack = new LinkedList<>(); + typeStack.add(info); + + while (!typeStack.isEmpty()) { + ClassInfo curInfo = typeStack.removeLast(); + for (MethodInfo curMethod : curInfo.getMethods()) { + if (!curMethod.getName().equalsIgnoreCase(methodName)) { + //name doesn't match + continue; + } + + String signature = curMethod.getSignature(); + if (matchingNameSignatures.contains(signature)) { + //this method is already in the matching name list + continue; + } + + matchingNameSignatures.add(signature); + matchingMethods.matchingName.add(curMethod); + + if (methodParameters == null) { + //user is not searching based on parameters + continue; + } + + List curParameters = curMethod.getParameters(); + if (curParameters.size() != methodParameters.size()) { + //parameter size doesn't match + continue; + } + + //check the parameters + boolean exactMatch = true; + for (int i = 0; i < curParameters.size(); i++) { + ParameterInfo curParameter = curParameters.get(i); + String curParameterName = curParameter.getType().getSimple() + (curParameter.isArray() ? "[]" : ""); + + String methodParameter = methodParameters.get(i); + + if (!curParameterName.equalsIgnoreCase(methodParameter)) { + //parameter types don't match + exactMatch = false; + break; + } + } + if (exactMatch) { + matchingMethods.exactSignature = curMethod; + } + } + + //add parent class to the stack + ClassName superClass = curInfo.getSuperClass(); + if (superClass != null) { + ClassInfo superClassInfo = dao.getClassInfo(superClass.getFullyQualified()); + if (superClassInfo != null) { + typeStack.add(superClassInfo); + } + } + + //add interfaces to the stack + for (ClassName interfaceName : curInfo.getInterfaces()) { + ClassInfo interfaceInfo = dao.getClassInfo(interfaceName.getFullyQualified()); + if (interfaceInfo != null) { + typeStack.add(interfaceInfo); + } + } + } + + return matchingMethods; + } + + /** + * Parses a javadoc chat command (e.g. "arraylist#add") + */ + static class CommandTextParser { + private final static Pattern messageRegex = Pattern.compile("(.*?)(\\((.*?)\\))?(#(.*?)(\\((.*?)\\))?)?(\\s+(.*?))?$"); + + private final String className, methodName; + private final List parameters; + private final int paragraph; + + /** + * @param message the command text + */ + public CommandTextParser(String message) { + Matcher m = messageRegex.matcher(message); + m.find(); + + className = m.group(1); + + if (m.group(2) != null) { //e.g. java.lang.string(string, string) + int dot = className.lastIndexOf('.'); + String simpleName = (dot < 0) ? className : className.substring(dot + 1); + methodName = simpleName; + } else { + methodName = m.group(5); //e.g. java.lang.string#indexOf(int) + } + + String parametersStr = m.group(4); //e.g. java.lang.string(string, string) + if (parametersStr == null || parametersStr.startsWith("#")) { + parametersStr = m.group(7); //e.g. java.lang.string#string(string, string) + if (parametersStr == null) { + parametersStr = m.group(3); + } + } + if (parametersStr == null || parametersStr.equals("*")) { + parameters = null; + } else if (parametersStr.isEmpty()) { + parameters = Collections.emptyList(); + } else { + parameters = Arrays.asList(parametersStr.split("\\s*,\\s*")); + } + + int paragraph; + try { + paragraph = Integer.parseInt(m.group(9)); + if (paragraph < 1) { + paragraph = 1; + } + } catch (NumberFormatException e) { + paragraph = 1; + } + this.paragraph = paragraph; + } + + public String getClassName() { + return className; + } + + public String getMethodName() { + return methodName; + } + + public List getParameters() { + return parameters; + } + + public int getParagraph() { + return paragraph; + } + } + + private static class MatchingMethods { + private MethodInfo exactSignature; + private final List matchingName = new ArrayList<>(); + + public boolean isEmpty() { + return exactSignature == null && matchingName.isEmpty(); + } + } + + private static class Paragraphs { + private final String paragraphs[]; + + public Paragraphs(String text) { + paragraphs = text.split("\n\n"); + } + + public int count() { + return paragraphs.length; + } + + public String get(int num) { + return paragraphs[num - 1]; + } + + /** + * Appends a paragraph to a {@link ChatBuilder}. + * @param num the paragraph number + * @param cb the chat builder + */ + public void append(int num, ChatBuilder cb) { + if (num > count()) { + num = count(); + } + + cb.append(get(num)); + if (count() > 1) { + cb.append(" (").append(num + "").append("/").append(count() + "").append(")"); + } + } + } } diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/JavadocDao.java b/src/main/java/com/gmail/inverseconduit/javadoc/JavadocDao.java index beb567e..b278c5f 100644 --- a/src/main/java/com/gmail/inverseconduit/javadoc/JavadocDao.java +++ b/src/main/java/com/gmail/inverseconduit/javadoc/JavadocDao.java @@ -1,50 +1,90 @@ package com.gmail.inverseconduit.javadoc; import java.io.IOException; -import java.util.ArrayList; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.List; +import java.util.Iterator; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import com.sun.nio.file.SensitivityWatchEventModifier; /** - * Retrieves class information from Javadoc files. + * Retrieves class information from Javadoc ZIP files, generated by + * OakbotDoclet. * @author Michael Angstadt */ public class JavadocDao { - private final Multimap simpleToFullClassNames = HashMultimap.create(); - private final Map cache = Collections.synchronizedMap(new HashMap<>()); - private final List libraries = new ArrayList<>(); + private static final Logger logger = Logger.getLogger(JavadocDao.class.getName()); /** - * Adds a library's Javadoc API to this DAO. - * @param loader the page loader - * @param parser the page parser - * @throws IOException if there's a problem reading from the Javadocs + * Maps each Javadoc ZIP file to the list of classes it contains. */ - public void addJavadocApi(PageLoader loader, PageParser parser) throws IOException { - addJavadocApi(new JavadocLibrary(loader, parser)); + private final Multimap libraryClasses = HashMultimap.create(); + + /** + * Maps class name aliases to their fully qualified names. For example, maps + * "string" to "java.lang.String". Note that there can be more than one + * class name mapped to an alias (for example "list" is mapped to + * "java.util.List" and "java.awt.List"). + */ + private final Multimap aliases = HashMultimap.create(); + + /** + * Caches class info that was parsed from a Javadoc ZIP file. The key is the + * fully-qualified name of the class, and the value is the parsed Javadoc + * info. + */ + private final Map cache = new HashMap<>(); + + /** + * @param dir the path to where the Javadoc ZIP files are stored. + * @throws IOException if there's a problem reading the ZIP files + */ + public JavadocDao(Path dir) throws IOException { + try (DirectoryStream stream = Files.newDirectoryStream(dir, JavadocDao::isZipFile)) { + for (Path path : stream) { + addApi(path); + } + } + + WatchThread watchThread = new WatchThread(dir); + watchThread.start(); } /** - * Adds a library's Javadoc API to this DAO. - * @param library the Javadoc library - * @throws IOException if there's a problem reading from the parser + * Adds a Javadoc ZIP file to the DAO. + * @param zipFile the zip file (generated by OakbotDoclet) + * @throws IOException if there was a problem reading the ZIP file */ - public void addJavadocApi(JavadocLibrary library) throws IOException { + private void addApi(Path zipFile) throws IOException { //add all the class names to the simple name index - for (String fullName : library.getAllClassNames()) { - int dotPos = fullName.lastIndexOf('.'); - String simpleName = fullName.substring(dotPos + 1); + LibraryZipFile zip = new LibraryZipFile(zipFile); + Iterator it = zip.getClasses(); + synchronized (this) { + while (it.hasNext()) { + ClassName className = it.next(); + String fullName = className.getFullyQualified(); + String simpleName = className.getSimple(); - simpleToFullClassNames.put(simpleName.toLowerCase(), fullName); + aliases.put(simpleName.toLowerCase(), fullName); + aliases.put(simpleName, fullName); + aliases.put(fullName.toLowerCase(), fullName); + aliases.put(fullName, fullName); + libraryClasses.put(zip, fullName); + } } - - libraries.add(library); } /** @@ -56,29 +96,34 @@ public void addJavadocApi(JavadocLibrary library) throws IOException { * @throws MultipleClassesFoundException if a simple name was passed into * this method and multiple classes were found that have that name */ - public ClassInfo getClassInfo(String className) throws IOException, MultipleClassesFoundException { - //convert simple name to fully-qualified name - if (!className.contains(".")) { - Collection names = simpleToFullClassNames.get(className.toLowerCase()); - if (names.isEmpty()) { - return null; - } + public synchronized ClassInfo getClassInfo(String className) throws IOException, MultipleClassesFoundException { + Collection names = aliases.get(className); + if (names.isEmpty()) { + //try case-insensitive search + names = aliases.get(className.toLowerCase()); + } - if (names.size() > 1) { - throw new MultipleClassesFoundException(names); - } + if (names.isEmpty()) { + //no class found + return null; + } - className = names.iterator().next(); + if (names.size() > 1) { + //multiple classes found + throw new MultipleClassesFoundException(names); } + className = names.iterator().next(); + //check the cache ClassInfo info = cache.get(className); if (info != null) { return info; } - for (JavadocLibrary library : libraries) { - info = library.getClassInfo(className); + //parse the class info from the Javadocs + for (LibraryZipFile zip : libraryClasses.keys()) { + info = zip.getClassInfo(className); if (info != null) { cache.put(className, info); return info; @@ -87,4 +132,110 @@ public ClassInfo getClassInfo(String className) throws IOException, MultipleClas return null; } + + private class WatchThread extends Thread { + private final Path dir; + private final WatchService watcher; + + /** + * @param dir the directory to watch + * @throws IOException if there's a problem watching the directory + */ + public WatchThread(Path dir) throws IOException { + setName(getClass().getSimpleName()); + setDaemon(true); + + this.dir = dir; + watcher = FileSystems.getDefault().newWatchService(); + dir.register(watcher, new WatchEvent.Kind[] { StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY }, SensitivityWatchEventModifier.HIGH); + } + + @Override + public void run() { + while (true) { + WatchKey key; + try { + key = watcher.take(); + } catch (InterruptedException e) { + return; + } + + for (WatchEvent event : key.pollEvents()) { + WatchEvent.Kind kind = event.kind(); + if (kind == StandardWatchEventKinds.OVERFLOW) { + continue; + } + + @SuppressWarnings("unchecked") + Path file = ((WatchEvent) event).context(); + if (!isZipFile(file)) { + continue; + } + + file = dir.resolve(file); + + if (kind == StandardWatchEventKinds.ENTRY_CREATE) { + add(file); + continue; + } + + if (kind == StandardWatchEventKinds.ENTRY_DELETE) { + remove(file); + continue; + } + + if (kind == StandardWatchEventKinds.ENTRY_MODIFY) { + remove(file); + add(file); + continue; + } + } + + boolean valid = key.reset(); + if (!valid) { + break; + } + } + } + + private void add(Path file) { + logger.info("Loading ZIP file " + file + "..."); + try { + addApi(file); + logger.info("ZIP file " + file + " loaded."); + } catch (IOException e) { + logger.log(Level.SEVERE, "Could not parse Javadoc ZIP file. ZIP file was not added to the JavadocDao.", e); + } + } + + private void remove(Path file) { + logger.info("Removing ZIP file " + file + "..."); + Path fileName = file.getFileName(); + + synchronized (JavadocDao.this) { + //find the corresponding LibraryZipFile object + LibraryZipFile found = null; + for (LibraryZipFile zip : libraryClasses.keys()) { + if (zip.getPath().getFileName().equals(fileName)) { + found = zip; + break; + } + } + if (found == null) { + logger.warning("Tried to remove ZIP file \"" + file + "\", but it was not found in the JavadocDao."); + return; + } + + Collection classNames = libraryClasses.removeAll(found); + aliases.values().removeAll(classNames); + cache.keySet().removeAll(classNames); + } + + logger.info("ZIP file " + file + " removed."); + } + } + + private static boolean isZipFile(Path file) { + return file.getFileName().toString().toLowerCase().endsWith(".zip"); + } } diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/JavadocLibrary.java b/src/main/java/com/gmail/inverseconduit/javadoc/JavadocLibrary.java deleted file mode 100644 index a69780f..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/JavadocLibrary.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.List; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; - -/** - * Represents a Javadoc library. - * @author Michael Angstadt - */ -public class JavadocLibrary { - private final PageLoader loader; - private final PageParser parser; - - /** - * @param loader the page loader - * @param parser the page parser - */ - public JavadocLibrary(PageLoader loader, PageParser parser) { - this.loader = loader; - this.parser = parser; - } - - /** - * Gets the fully-qualified names of all the classes that are contained - * within this library. - * @return the fully-qualified class names - * @throws IOException if there was a problem reading from the Javadocs - */ - public List getAllClassNames() throws IOException { - Document document; - try (InputStream in = loader.getAllClassesFile()) { - if (null == in) { - return Collections.EMPTY_LIST; - } - document = Jsoup.parse(in, "UTF-8", parser.getBaseUrl()); - } - return parser.parseClassNames(document); - } - - /** - * Gets the Javadoc documentation of a class. - * @param className the fully-qualified class name (e.g. "java.lang.String") - * @return the documentation or null if the class was not found - * @throws IOException if there was a problem reading from the Javadocs - */ - public ClassInfo getClassInfo(String className) throws IOException { - Document document; - try (InputStream in = loader.getClassPage(className)) { - if (null == in) { - return null; - } - document = Jsoup.parse(in, "UTF-8", parser.getBaseUrl()); - } - return parser.parseClassPage(document, className); - } -} \ No newline at end of file diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/JsoupPageParser.java b/src/main/java/com/gmail/inverseconduit/javadoc/JsoupPageParser.java deleted file mode 100644 index ad50e26..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/JsoupPageParser.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; - -/** - * Parses the Jsoup Javadocs. - * @author Michael Angstadt - */ -public class JsoupPageParser implements PageParser { - @Override - public List parseClassNames(Document document) { - List classNames = new ArrayList<>(); - for (Element element : document.select("a")) { - String url = element.attr("href"); - int dotPos = url.lastIndexOf('.'); - if (dotPos < 0) { - continue; - } - - url = url.substring(0, dotPos); - url = url.replace('/', '.'); - classNames.add(url); - } - return classNames; - } - - @Override - public ClassInfo parseClassPage(Document document, String className) { - String description; - { - JsoupDescriptionNodeVisitor visitor = new JsoupDescriptionNodeVisitor(); - document.traverse(visitor); - description = visitor.getStringBuilder().toString().trim(); - } - - String url = getBaseUrl() + "?" + className.replace('.', '/') + ".html"; - - List modifiers; - { - Element element = document.select("dt").get(1); - modifiers = Arrays.asList(element.text().trim().split(" ")); - String simpleName = className.substring(className.lastIndexOf('.') + 1); - int pos = modifiers.indexOf(simpleName); - modifiers = modifiers.subList(0, pos); - } - - return new ClassInfo(className, description, url, modifiers, false); - } - - @Override - public String getBaseUrl() { - return "http://jsoup.org/apidocs/"; - } - - private static class JsoupDescriptionNodeVisitor extends DescriptionNodeVisitor { - private Boolean inDescription; - - @Override - public void head(Node node, int depth) { - if (inDescription == Boolean.FALSE) { - return; - } - - if (inDescription == null) { - if ("p".equals(node.nodeName())) { - //the first

signals the start of the description - inDescription = Boolean.TRUE; - } else { - return; - } - } - - if ("dl".equals(node.nodeName())) { - inDescription = false; - return; - } - - super.head(node, depth); - } - - @Override - public void tail(Node node, int depth) { - if (inDescription == Boolean.TRUE) { - super.head(node, depth); - } - } - } -} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/LibraryZipFile.java b/src/main/java/com/gmail/inverseconduit/javadoc/LibraryZipFile.java new file mode 100644 index 0000000..0a9dd57 --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/javadoc/LibraryZipFile.java @@ -0,0 +1,232 @@ +package com.gmail.inverseconduit.javadoc; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import com.gmail.inverseconduit.utils.DocumentWrapper; + +/** + * Represents a ZIP file that was generated by OakbotDoclet, which contains + * Javadoc information. + * @author Michael Angstadt + */ +public class LibraryZipFile { + private static final String EXTENSION = ".xml"; + private static final String INFO_FILENAME = "info" + EXTENSION; + + private final Path file; + private final String baseUrl; + private final String name; + private final String version; + private final String projectUrl; + + public LibraryZipFile(Path file) throws IOException { + this.file = file.toRealPath(); + + try (FileSystem fs = FileSystems.newFileSystem(file, null)) { + Path info = fs.getPath("/" + INFO_FILENAME); + if (!Files.exists(info)) { + baseUrl = name = version = projectUrl = null; + return; + } + + Element infoElement; + try (InputStream in = Files.newInputStream(info)) { + DocumentWrapper document = new DocumentWrapper(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in)); + infoElement = document.element("/info"); + } catch (ParserConfigurationException e) { + //should never be thrown + throw new RuntimeException(e); + } catch (SAXException e) { + //XML parse error + throw new IOException(e); + } + + if (infoElement == null) { + baseUrl = name = version = projectUrl = null; + return; + } + + String name = infoElement.getAttribute("name"); + this.name = name.isEmpty() ? null : name; + + String baseUrl = infoElement.getAttribute("baseUrl"); + if (baseUrl.isEmpty()) { + this.baseUrl = null; + } else { + this.baseUrl = baseUrl + (baseUrl.endsWith("/") ? "" : "/"); + } + + String projectUrl = infoElement.getAttribute("projectUrl"); + this.projectUrl = projectUrl.isEmpty() ? null : projectUrl; + + String version = infoElement.getAttribute("version"); + this.version = version.isEmpty() ? null : version; + } + } + + /** + * Gets the URL to a class's Javadoc page (with frames). + * @param info the class + * @return the URL or null if no base URL was given + */ + public String getFrameUrl(ClassInfo info) { + if (baseUrl == null) { + return null; + } + return baseUrl + "index.html?" + info.getName().getFullyQualified().replace('.', '/') + ".html"; + } + + /** + * Gets the URL to a class's Javadoc page (without frames). + * @param info the class + * @return the URL or null if no base URL was given + */ + public String getUrl(ClassInfo info) { + if (baseUrl == null) { + return null; + } + return baseUrl + info.getName().getFullyQualified().replace('.', '/') + ".html"; + } + + /** + * Gets a list of all classes that are in the library. + * @return the fully-qualified names of all the classes + * @throws IOException if there's a problem reading the ZIP file + */ + public Iterator getClasses() throws IOException { + final FileSystem fs = FileSystems.newFileSystem(file, null); + final DirectoryStream stream = Files.newDirectoryStream(fs.getPath("/"), entry -> { + String name = entry.getFileName().toString(); + if (!name.endsWith(EXTENSION)) { + return false; + } + + return !name.equals(INFO_FILENAME); + }); + + final Iterator it = stream.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + boolean hasNext = it.hasNext(); + if (!hasNext) { + try { + stream.close(); + } catch (IOException e) { + //ignore + } + try { + fs.close(); + } catch (IOException e) { + //ignore + } + } + + return hasNext; + } + + @Override + public ClassName next() { + Path file = it.next(); + String fileName = file.getFileName().toString(); + String fullName = fileName.substring(0, fileName.length() - EXTENSION.length()); + return new ClassName(fullName); + } + }; + } + + /** + * Gets the parsed XML DOM of the given class. + * @param fullName the fully-qualifed class name (e.g. "java.lang.String") + * @return the XML DOM or null if the class was not found + * @throws IOException if there was a problem reading from the ZIP file or + * parsing the XML + */ + public ClassInfo getClassInfo(String fullName) throws IOException { + try (FileSystem fs = FileSystems.newFileSystem(file, null)) { + Path path = fs.getPath(fullName + EXTENSION); + if (!Files.exists(path)) { + return null; + } + + Document document; + try (InputStream in = Files.newInputStream(path)) { + document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); + } catch (SAXException | ParserConfigurationException e) { + throw new IOException(e); + } + + return new ClassInfoXmlParser(document, this).parse(); + } + } + + /** + * Gets the base URL of this library's Javadocs. + * @return the base URL or null if none was defined + */ + public String getBaseUrl() { + return baseUrl; + } + + /** + * Gets the name of this library. + * @return the name (e.g. "jsoup") or null if none was defined + */ + public String getName() { + return name; + } + + /** + * Gets the version number of this library. + * @return the version number (e.g. "1.8.1") or null if none was defined + */ + public String getVersion() { + return version; + } + + /** + * Gets the URL to the library's webpage. + * @return the URL or null if none was defined + */ + public String getProjectUrl() { + return projectUrl; + } + + public Path getPath() { + return file; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((file == null) ? 0 : file.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + LibraryZipFile other = (LibraryZipFile) obj; + if (file == null) { + if (other.file != null) return false; + } else if (!file.equals(other.file)) return false; + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/MethodInfo.java b/src/main/java/com/gmail/inverseconduit/javadoc/MethodInfo.java new file mode 100644 index 0000000..50a5aa3 --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/javadoc/MethodInfo.java @@ -0,0 +1,186 @@ +package com.gmail.inverseconduit.javadoc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +/** + * Contains information on a method. + * @author Michael Angstadt + */ +public class MethodInfo { + private final String name; + private final String description; + private final String urlAnchor; + private final Set modifiers; + private final List parameters; + private final ClassName returnValue; + private final boolean deprecated; + + private MethodInfo(Builder builder) { + name = builder.name; + modifiers = builder.modifiers.build(); + parameters = builder.parameters.build(); + description = builder.description; + returnValue = builder.returnValue; + deprecated = builder.deprecated; + + StringBuilder sb = new StringBuilder(); + sb.append(name).append('-'); + List fullNames = new ArrayList<>(); + for (ParameterInfo parameter : parameters) { + String fullName = parameter.getType().getFullyQualified(); + if (parameter.isArray()) { + fullName += ":A"; + } + if (parameter.isVarargs()) { + fullName += "..."; + } + fullNames.add(fullName); + } + sb.append(String.join("-", fullNames)); + sb.append('-'); + urlAnchor = sb.toString(); + } + + /** + * Gets the method name + * @return the method name + */ + public String getName() { + return name; + } + + /** + * Gets the method's parameters + * @return the method's parameters + */ + public List getParameters() { + return parameters; + } + + /** + * Gets the method's Javadoc description. + * @return the description in SO-Chat Markdown syntax + */ + public String getDescription() { + return description; + } + + /** + * Gets the method modifiers. + * @return the method modifiers (e.g. "public", "static") + */ + public Collection getModifiers() { + return modifiers; + } + + /** + * Gets this method's Javadoc URL anchor. + * @return the URL anchor (e.g. "substring-int-int-") + */ + public String getUrlAnchor() { + return urlAnchor; + } + + /** + * Gets whether this method is deprecated or not. + * @return true if it's deprecated, false if not + */ + public boolean isDeprecated() { + return deprecated; + } + + /** + * Gets a string that uniquely identifies the method signature, as the + * compiler would. + * @return the signature string (only includes the method name and the + * fully-qualified names of the parameters, e.g. "substring(int, int)") + */ + public String getSignature() { + List params = new ArrayList<>(); + for (ParameterInfo parameter : parameters) { + params.add(parameter.getType().getFullyQualified() + (parameter.isArray() ? "[]" : "")); + } + return name + "(" + String.join(", ", params) + ")"; + } + + /** + * Gets the signature string to display in the chat. + * @return the signature string for the chat + */ + public String getSignatureString() { + StringBuilder sb = new StringBuilder(); + + if (returnValue != null) { + sb.append(returnValue.getSimple()).append(' '); + } + sb.append(name); + + List params = new ArrayList<>(); + for (ParameterInfo parameter : parameters) { + String type = parameter.getType().getSimple(); + String generic = parameter.getGeneric(); + if (generic != null) { + type += generic; + } + if (parameter.isArray()) { + type += "[]"; + } + if (parameter.isVarargs()) { + type += "..."; + } + params.add(type + " " + parameter.getName()); + } + sb.append('(').append(String.join(", ", params)).append(')'); + + return sb.toString(); + } + + public static class Builder { + private String name; + private ImmutableSet.Builder modifiers = ImmutableSet.builder(); + private ImmutableList.Builder parameters = ImmutableList.builder(); + private String description; + private ClassName returnValue; + private boolean deprecated; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder modifiers(Collection modifiers) { + this.modifiers.addAll(modifiers); + return this; + } + + public Builder parameter(ParameterInfo parameter) { + this.parameters.add(parameter); + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder returnValue(ClassName returnValue) { + this.returnValue = returnValue; + return this; + } + + public Builder deprecated(boolean deprecated) { + this.deprecated = deprecated; + return this; + } + + public MethodInfo build() { + return new MethodInfo(this); + } + } +} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/PageLoader.java b/src/main/java/com/gmail/inverseconduit/javadoc/PageLoader.java deleted file mode 100644 index 696bf35..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/PageLoader.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Loads Javadoc HTML pages - * @author Michael Angstadt - */ -public interface PageLoader { - /** - * Gets an input stream to the HTML page for a given class. - * @param className the fully qualified class name (e.g. "java.lang.String") - * @return an input stream to the HTML page or null if the class was not - * found - * @throws IOException if there's a problem reading the page - */ - InputStream getClassPage(String className) throws IOException; - - /** - * Gets the HTML file that list all the classes. - * @return an input stream to the HTML file - * @throws IOException if there's a problem reading the file - */ - InputStream getAllClassesFile() throws IOException; -} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/PageParser.java b/src/main/java/com/gmail/inverseconduit/javadoc/PageParser.java deleted file mode 100644 index 29eb751..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/PageParser.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import java.util.List; - -import org.jsoup.nodes.Document; - -/** - * Parses Javadoc HTML pages. - * @author Michael Angstadt - */ -public interface PageParser { - /** - * Parses the fully-qualified names of all the classes. - * @param document the HTML document - * @return the fully qualified names of each class names - */ - public List parseClassNames(Document document); - - /** - * Parses a class page. - * @param document the HTML document - * @param className the fully-qualified class name - * @return the class info - */ - public ClassInfo parseClassPage(Document document, String className); - - /** - * Gets the base URL to use when parsing the document. - * @return the base URL - */ - public String getBaseUrl(); -} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/ParameterInfo.java b/src/main/java/com/gmail/inverseconduit/javadoc/ParameterInfo.java new file mode 100644 index 0000000..7899e75 --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/javadoc/ParameterInfo.java @@ -0,0 +1,41 @@ +package com.gmail.inverseconduit.javadoc; + +/** + * Contains information on a method parameter. + * @author Michael Angstadt + */ +public class ParameterInfo { + private final ClassName type; + private final String name; + private final String generic; + private final boolean array; + private final boolean varargs; + + public ParameterInfo(ClassName type, String name, boolean array, boolean varargs, String generic) { + this.type = type; + this.name = name; + this.array = array; + this.varargs = varargs; + this.generic = generic; + } + + public ClassName getType() { + return type; + } + + public String getName() { + return name; + } + + public boolean isArray() { + return array; + } + + public boolean isVarargs() { + return varargs; + } + + public String getGeneric() { + return generic; + } +} diff --git a/src/main/java/com/gmail/inverseconduit/javadoc/ZipPageLoader.java b/src/main/java/com/gmail/inverseconduit/javadoc/ZipPageLoader.java deleted file mode 100644 index 6f1e8b7..0000000 --- a/src/main/java/com/gmail/inverseconduit/javadoc/ZipPageLoader.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import static org.apache.commons.io.IOUtils.closeQuietly; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -/** - * Loads Javadoc pages from a ZIP file. The root the ZIP file must contain the - * "allclasses-frame.html" file. - * @author Michael Angstadt - */ -public class ZipPageLoader implements PageLoader { - private static final String allClassesFrameFileName = "allclasses-frame.html"; - private final Path file; - - /** - * @param file the path to the ZIP file. - * @throws IOException if there's a problem reading the ZIP file - * @throws IllegalArgumentException if there is no "allclasses-frame.html" - * file at the root - */ - public ZipPageLoader(Path file) throws IOException { - //make sure it contains the "allclasses-frame.html" file - try (FileSystem fs = FileSystems.newFileSystem(file, null)) { - Path allClassesFile = fs.getPath("/" + allClassesFrameFileName); - if (!Files.exists(allClassesFile)) { - throw new IllegalArgumentException("\"" + allClassesFrameFileName + "\" not found in ZIP root."); - } - } - - this.file = file; - } - - @Override - public InputStream getClassPage(String className) throws IOException { - Path htmlFile = Paths.get("/" + className.replace('.', '/') + ".html"); - FileSystem fs = FileSystems.newFileSystem(file, null); - Path path = fs.getPath(htmlFile.toString()); - if (Files.exists(path)) { - return new ZipFileInputStream(fs, path); - } - - fs.close(); - return null; - } - - @Override - public InputStream getAllClassesFile() throws IOException { - FileSystem fs = FileSystems.newFileSystem(file, null); - Path allClassesFile = fs.getPath("/" + allClassesFrameFileName); - return new ZipFileInputStream(fs, allClassesFile); - } - - private static class ZipFileInputStream extends InputStream { - private final FileSystem fs; - private final InputStream in; - - public ZipFileInputStream(FileSystem fs, Path path) throws IOException { - this.fs = fs; - this.in = Files.newInputStream(path); - } - - @Override - public int read() throws IOException { - return in.read(); - } - - @Override - public void close() throws IOException { - closeQuietly(in); - closeQuietly(fs); - } - } -} diff --git a/src/main/java/com/gmail/inverseconduit/utils/ChatBuilder.java b/src/main/java/com/gmail/inverseconduit/utils/ChatBuilder.java new file mode 100644 index 0000000..c37e5e4 --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/utils/ChatBuilder.java @@ -0,0 +1,196 @@ +package com.gmail.inverseconduit.utils; + +import com.gmail.inverseconduit.datatype.ChatMessage; + +/** + * Helper class for building chat messages that have SO Chat markdown. + * @author Michael Angstadt + * @see Formatting + * FAQ + */ +public class ChatBuilder implements CharSequence { + private final StringBuilder sb; + + /** + * Creates a new chat builder. + */ + public ChatBuilder() { + sb = new StringBuilder(); + } + + /** + * Creates a new chat builder. + * @param text the string to populate it with + */ + public ChatBuilder(String text) { + sb = new StringBuilder(text); + } + + /** + * Appends the character sequence for "fixed font". + * @return this + */ + public ChatBuilder fixed() { + return append(" "); + } + + /** + * Appends the character sequence for "bold". + * @return this + */ + public ChatBuilder bold() { + return append("**"); + } + + /** + * Wraps text in "bold" formatting. + * @param text the text to wrap + * @return this + */ + public ChatBuilder bold(String text) { + return bold().append(text).bold(); + } + + /** + * Appends the character sequence for "code". + * @return this + */ + public ChatBuilder code() { + return append('`'); + } + + /** + * Wraps text in "code" formatting. + * @param text the text to wrap + * @return this + */ + public ChatBuilder code(String text) { + return code().append(text).code(); + } + + /** + * Appends the character sequence for "italic". + * @return this + */ + public ChatBuilder italic() { + return append('*'); + } + + /** + * Wraps text in "italic" formatting. + * @param text the text to wrap + * @return this + */ + public ChatBuilder italic(String text) { + return italic().append(text).italic(); + } + + /** + * Appends a clickable link. + * @param display the display text + * @param url the URL + * @return this + */ + public ChatBuilder link(String display, String url) { + return link(display, url, null); + } + + /** + * Appends a clickable link. + * @param display the display text + * @param url the URL + * @param title the link title or null/empty for no title + * @return this + */ + public ChatBuilder link(String display, String url, String title) { + append('[').append(display.trim()).append("](").append(url.trim()); + if (title != null && !title.isEmpty()) { + append(" \"").append(title.trim()).append('"'); + } + return append(')'); + } + + /** + * Appends a newline character. + * @return this + */ + public ChatBuilder nl() { + return append('\n'); + } + + /** + * Appends the "reply to message" syntax. + * @param message the message to reply to + * @return this + */ + public ChatBuilder reply(ChatMessage message) { + //TODO return append(':').append(message.getMessageId() + "").append(' '); + return append('@').append(message.getUsername()).append(' '); + } + + /** + * Appends the character sequence for "strike through". + * @return this + */ + public ChatBuilder strike() { + return append("---"); + } + + /** + * Wraps text in "strike through" formatting. + * @param text the text to wrap + * @return this + */ + public ChatBuilder strike(String text) { + return strike().append(text).strike(); + } + + /** + * Appends a tag. + * @param tag the tag name + * @return this + */ + public ChatBuilder tag(String tag) { + return append("[tag:").append(tag).append(']'); + } + + /** + * Appends a raw character. + * @param c the character to append + * @return this + */ + public ChatBuilder append(char c) { + sb.append(c); + return this; + } + + /** + * Appends a raw string. + * @param text the string to append + * @return this + */ + public ChatBuilder append(CharSequence text) { + sb.append(text); + return this; + } + + @Override + public int length() { + return sb.length(); + } + + @Override + public char charAt(int index) { + return sb.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return sb.subSequence(start, end); + } + + @Override + public String toString() { + return sb.toString(); + } +} diff --git a/src/main/java/com/gmail/inverseconduit/utils/DocumentWrapper.java b/src/main/java/com/gmail/inverseconduit/utils/DocumentWrapper.java new file mode 100644 index 0000000..298154d --- /dev/null +++ b/src/main/java/com/gmail/inverseconduit/utils/DocumentWrapper.java @@ -0,0 +1,107 @@ +package com.gmail.inverseconduit.utils; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Wraps an XML {@link Node} object, providing utility functionality. + * @author Michael Angstadt + */ +public class DocumentWrapper { + private final Node root; + private final XPath xpath; + + /** + * @param root the node to wrap + */ + public DocumentWrapper(Node root) { + this.root = root; + xpath = XPathFactory.newInstance().newXPath(); + } + + /** + * Queries the DOM for a specific element. + * @param query the xpath query + * @return the element or null if not found + */ + public Element element(String query) { + return element(query, root); + } + + /** + * Queries the DOM for a specific element. + * @param query the xpath query + * @param from the node to look inside of + * @return the element or null if not found + */ + public Element element(String query, Node from) { + try { + return (Element) xpath.evaluate(query, from, XPathConstants.NODE); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + /** + * Queries the DOM for multiple elements. + * @param query the xpath query + * @return the elements + */ + public List elements(String query) { + return elements(query, root); + } + + /** + * Queries the DOM for multiple elements. + * @param query the xpath query + * @param from the node to look inside of + * @return the elements + */ + public List elements(String query, Node from) { + NodeList list; + try { + list = (NodeList) xpath.evaluate(query, from, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + + List elements = new ArrayList<>(list.getLength()); + for (int i = 0; i < list.getLength(); i++) { + elements.add((Element) list.item(i)); + } + return elements; + } + + @Override + public String toString() { + try { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + + StringWriter sw = new StringWriter(); + DOMSource source = new DOMSource(root); + StreamResult result = new StreamResult(sw); + transformer.transform(source, result); + return sw.toString(); + } catch (TransformerException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/CommandTextParserTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/CommandTextParserTest.java new file mode 100644 index 0000000..4a7982d --- /dev/null +++ b/src/test/java/com/gmail/inverseconduit/javadoc/CommandTextParserTest.java @@ -0,0 +1,123 @@ +package com.gmail.inverseconduit.javadoc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; + +import org.junit.Test; + +import com.gmail.inverseconduit.javadoc.JavaDocAccessor.CommandTextParser; + +/** + * @author Michael Angstadt + */ +public class CommandTextParserTest { + @Test + public void onMessage() { + CommandTextParser parser = new CommandTextParser("java.lang.string"); + assertEquals("java.lang.string", parser.getClassName()); + assertNull(parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(*)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf()"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList(), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("int"), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int[])"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("int[]"), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int...)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("int..."), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int, int)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("int", "int"), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int, int) 2"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(2, parser.getParagraph()); + assertEquals(Arrays.asList("int", "int"), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string#indexOf(int,int)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("indexOf", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("int", "int"), parser.getParameters()); + + parser = new CommandTextParser("string()"); + assertEquals("string", parser.getClassName()); + assertEquals("string", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList(), parser.getParameters()); + + parser = new CommandTextParser("string(string)"); + assertEquals("string", parser.getClassName()); + assertEquals("string", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("string"), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string()"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("string", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList(), parser.getParameters()); + + parser = new CommandTextParser("java.lang.string(string)"); + assertEquals("java.lang.string", parser.getClassName()); + assertEquals("string", parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertEquals(Arrays.asList("string"), parser.getParameters()); + } + + @Test + public void onMessage_invalid_paragraph() { + CommandTextParser parser = new CommandTextParser("java.lang.string foo"); + assertEquals("java.lang.string", parser.getClassName()); + assertNull(parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + + parser = new CommandTextParser("java.lang.string -1"); + assertEquals("java.lang.string", parser.getClassName()); + assertNull(parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + + parser = new CommandTextParser("java.lang.string 1.2"); + assertEquals("java.lang.string", parser.getClassName()); + assertNull(parser.getMethodName()); + assertEquals(1, parser.getParagraph()); + assertNull(parser.getParameters()); + } +} diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/Java8PageParserTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/Java8PageParserTest.java deleted file mode 100644 index 2196595..0000000 --- a/src/test/java/com/gmail/inverseconduit/javadoc/Java8PageParserTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.List; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Test; - -/** - * @author Michael Angstadt - */ -public class Java8PageParserTest { - - @Test - public void getAllClasses() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("java8-allclasses-frame.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - Java8PageParser parser = new Java8PageParser(); - List actual = parser.parseClassNames(document); - //@formatter:off - List expected = Arrays.asList( - "java.awt.List", - "java.lang.String", - "java.util.List", - "java.util.Map.Entry" - ); - //@formatter:on - assertEquals(expected, actual); - } - - @Test - public void getClassInfo() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("String.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - Java8PageParser parser = new Java8PageParser(); - ClassInfo info = parser.parseClassPage(document, "java.lang.String"); - assertEquals("java.lang.String", info.getFullName()); - assertEquals("https://docs.oracle.com/javase/8/docs/api/?java/lang/String.html", info.getUrl()); - - assertEquals(Arrays.asList("public", "final", "class"), info.getModifiers()); - assertFalse(info.isDeprecated()); - - //@formatter:off - assertEquals( - "The `String` class represents character strings.\n" + - " `code` text \n" + - " **bold** text\n" + - " **bold** text\n" + - " *italic* text\n" + - " *italic* text\n" + - " \\*asterisks\\*\n" + - " \\_underscores\\_\n" + - " \\[brackets\\]\n" + - " [Google Search](http://www.google.com \"with title\")\n" + - " [Bing Search](http://www.bing.com)\n" + - " Because String objects are immutable they can be shared. For example: \n" + - "\n" + - "\r\n" + - " String str = \"abc\";\r\n" + - "\n" + - "\n" + - " is equivalent to: \n" + - "\n" + - "\r\n" + - " char data[] = {'a', 'b', 'c'};\r\n" + - " String str = new String(data);\r\n" + - "\n" + - " \n" + - "ignore me", info.getDescription()); - //@formatter:on - } - - @Test - public void getClassInfo_annotation() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("SuppressWarnings.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - Java8PageParser parser = new Java8PageParser(); - ClassInfo info = parser.parseClassPage(document, "java.lang.SuppressWarnings"); - assertEquals("java.lang.SuppressWarnings", info.getFullName()); - assertEquals("https://docs.oracle.com/javase/8/docs/api/?java/lang/SuppressWarnings.html", info.getUrl()); - - assertEquals(Arrays.asList("public", "@interface"), info.getModifiers()); - assertFalse(info.isDeprecated()); - assertNotNull(info.getDescription()); - } - - @Test - public void getClassInfo_deprecated() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("StringBufferInputStream.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - Java8PageParser parser = new Java8PageParser(); - ClassInfo info = parser.parseClassPage(document, "java.io.StringBufferInputStream"); - assertEquals("java.io.StringBufferInputStream", info.getFullName()); - assertEquals("https://docs.oracle.com/javase/8/docs/api/?java/io/StringBufferInputStream.html", info.getUrl()); - - assertEquals(Arrays.asList("public", "class"), info.getModifiers()); - assertTrue(info.isDeprecated()); - System.out.println((int) info.getDescription().charAt(12)); - System.out.println((int) info.getDescription().charAt(13)); - assertNotNull(info.getDescription()); - } -} diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/JavadocDaoTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/JavadocDaoTest.java index 23b0ef6..184b701 100644 --- a/src/test/java/com/gmail/inverseconduit/javadoc/JavadocDaoTest.java +++ b/src/test/java/com/gmail/inverseconduit/javadoc/JavadocDaoTest.java @@ -1,139 +1,179 @@ package com.gmail.inverseconduit.javadoc; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.logging.LogManager; +import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; /** * @author Michael Angstadt */ public class JavadocDaoTest { - private final JavadocDao dao = new JavadocDao(); + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private final Path root = Paths.get("src", "test", "resources", "com", "gmail", "inverseconduit", "javadoc"); + private final JavadocDao dao; { try { - dao.addJavadocApi(new JavadocLibrary(null, null) { - @Override - public List getAllClassNames() throws IOException { - //@formatter:off - return Arrays.asList( - "javax.management.Attribute", - "javax.naming.directory.Attribute", - "java.lang.String" - ); - //@formatter:on - } - - @Override - public ClassInfo getClassInfo(String className) throws IOException { - if (className.startsWith("java.")) { - return new ClassInfo(className, "description - " + className, null, Collections.emptyList(), false); - } - return null; - } - }); - dao.addJavadocApi(new JavadocLibrary(null, null) { - @Override - public List getAllClassNames() throws IOException { - //@formatter:off - return Arrays.asList( - "org.jsoup.nodes.Attribute" - ); - //@formatter:on - } - - @Override - public ClassInfo getClassInfo(String className) throws IOException { - if (className.startsWith("org.")) { - return new ClassInfo(className, "description - " + className, null, Collections.emptyList(), false); - } - return null; - } - }); + dao = new JavadocDao(root); } catch (IOException e) { - //not thrown + throw new RuntimeException(e); } } + @BeforeClass + public static void beforeClass() { + //turn off logging + LogManager.getLogManager().reset(); + } + @Test public void simpleName_single_match() throws Exception { - ClassInfo info = dao.getClassInfo("String"); - assertEquals("java.lang.String", info.getFullName()); - assertEquals("description - java.lang.String", info.getDescription()); + ClassInfo info = dao.getClassInfo("Collection"); + assertEquals("java.util.Collection", info.getName().getFullyQualified()); } @Test public void simpleName_no_match() throws Exception { ClassInfo info = dao.getClassInfo("FooBar"); assertNull(info); - - info = dao.getClassInfo("freemarker.FooBar"); - assertNull(info); } @Test public void simpleName_case_insensitive() throws Exception { - ClassInfo info = dao.getClassInfo("string"); - assertEquals("java.lang.String", info.getFullName()); - assertEquals("description - java.lang.String", info.getDescription()); + ClassInfo info = dao.getClassInfo("collection"); + assertEquals("java.util.Collection", info.getName().getFullyQualified()); } @Test public void simpleName_multiple_matches() throws Exception { try { - ClassInfo info = dao.getClassInfo("Attribute"); - fail(info.getFullName()); + dao.getClassInfo("List"); + fail(); } catch (MultipleClassesFoundException e) { Set actual = new HashSet<>(e.getClasses()); - Set expected = new HashSet<>(Arrays.asList("javax.management.Attribute", "javax.naming.directory.Attribute", "org.jsoup.nodes.Attribute")); + Set expected = new HashSet<>(Arrays.asList("java.awt.List", "java.util.List")); assertEquals(expected, actual); } } @Test public void fullName() throws Exception { - ClassInfo info = dao.getClassInfo("org.jsoup.nodes.Attribute"); - assertEquals("org.jsoup.nodes.Attribute", info.getFullName()); - assertEquals("description - org.jsoup.nodes.Attribute", info.getDescription()); + ClassInfo info = dao.getClassInfo("java.util.List"); + assertEquals("java.util.List", info.getName().getFullyQualified()); + } + + @Test + public void fullName_case_insensitive() throws Exception { + ClassInfo info = dao.getClassInfo("java.util.list"); + assertEquals("java.util.List", info.getName().getFullyQualified()); + } + + @Test + public void directory_watcher_ignore_non_zip_files() throws Exception { + Path dir = temporaryFolder.getRoot().toPath(); + JavadocDao dao = new JavadocDao(dir); + + assertNull(dao.getClassInfo("java.util.List")); + + Path source = root.resolve("LibraryZipFileTest.zip"); + Path dest = dir.resolve("LibraryZipFileTest.txt"); + Files.copy(source, dest); + Thread.sleep(1000); + assertNull(dao.getClassInfo("java.util.List")); } @Test - public void cache() throws Exception { - JavadocLibrary spy = spy(new JavadocLibrary(null, null) { - @Override - public List getAllClassNames() throws IOException { - //@formatter:off - return Arrays.asList( - "java.lang.String" - ); - //@formatter:on - } - - @Override - public ClassInfo getClassInfo(String className) throws IOException { - if (className.startsWith("java.")) { - return new ClassInfo(className, "description - " + className, null, Collections.emptyList(), false); - } - return null; - } - }); - - JavadocDao dao = new JavadocDao(); - dao.addJavadocApi(spy); - - dao.getClassInfo("String"); - dao.getClassInfo("string"); - verify(spy, times(1)).getClassInfo("java.lang.String"); + public void directory_watcher_add() throws Exception { + Path dir = temporaryFolder.getRoot().toPath(); + JavadocDao dao = new JavadocDao(dir); + + assertNull(dao.getClassInfo("java.util.List")); + + Path source = root.resolve("LibraryZipFileTest.zip"); + Path dest = dir.resolve("LibraryZipFileTest.zip"); + Files.copy(source, dest); + + //wait for the WatchService to pick up the file + //this is really slow on Macs, see: http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else + long start = System.currentTimeMillis(); + ClassInfo info = null; + while (info == null && (System.currentTimeMillis() - start) < TimeUnit.SECONDS.toMillis(5)) { + Thread.sleep(200); + info = dao.getClassInfo("java.util.List"); + } + assertNotNull(info); + } + + @Test + public void directory_watcher_remove() throws Exception { + Path dir = temporaryFolder.getRoot().toPath(); + Path source = root.resolve("LibraryZipFileTest.zip"); + Path dest = dir.resolve("LibraryZipFileTest.zip"); + Files.copy(source, dest); + + JavadocDao dao = new JavadocDao(dir); + + ClassInfo info = dao.getClassInfo("java.util.List"); + assertNotNull(info); + + source = dir.resolve("LibraryZipFileTest.zip"); + Files.delete(source); + + //wait for the WatchService to pick up the deleted file + //this is really slow on Macs, see: http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else + long start = System.currentTimeMillis(); + while (info != null && (System.currentTimeMillis() - start) < TimeUnit.SECONDS.toMillis(5)) { + Thread.sleep(200); + info = dao.getClassInfo("java.util.List"); + } + assertNull(info); + } + + @Test + public void directory_watcher_modified() throws Exception { + Path dir = temporaryFolder.getRoot().toPath(); + Path source = root.resolve("LibraryZipFileTest.zip"); + Path dest = dir.resolve("LibraryZipFileTest.zip"); + Files.copy(source, dest); + + Thread.sleep(1500); //wait a bit before modifying the file so the timestamp is significantly different (for Macs) + + JavadocDao dao = new JavadocDao(dir); + + ClassInfo info = dao.getClassInfo("java.util.List"); + assertNotNull(info); + + try (FileSystem fs = FileSystems.newFileSystem(dest, null)) { + Path path = fs.getPath("java.util.List.xml"); + Files.delete(path); + } + + //wait for the WatchService to pick up the change + //this is really slow on Macs, see: http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else + long start = System.currentTimeMillis(); + while (info != null && (System.currentTimeMillis() - start) < TimeUnit.SECONDS.toMillis(5)) { + Thread.sleep(200); + info = dao.getClassInfo("java.util.List"); + } + assertNull(info); } } diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/JsoupPageParserTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/JsoupPageParserTest.java deleted file mode 100644 index fa1c87c..0000000 --- a/src/test/java/com/gmail/inverseconduit/javadoc/JsoupPageParserTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.List; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Test; - -/** - * @author Michael Angstadt - */ -public class JsoupPageParserTest { - @Test - public void getAllClasses() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("jsoup-allclasses-frame.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - JsoupPageParser parser = new JsoupPageParser(); - List actual = parser.parseClassNames(document); - //@formatter:off - List expected = Arrays.asList( - "org.jsoup.nodes.Attribute", - "org.jsoup.nodes.Attributes", - "org.jsoup.nodes.Comment", - "org.jsoup.Connection", - "org.jsoup.Connection.Base" - ); - //@formatter:on - assertEquals(expected, actual); - } - - @Test - public void getClassInfo() throws Exception { - Document document; - try (InputStream in = getClass().getResourceAsStream("Attribute.html")) { - document = Jsoup.parse(in, "UTF-8", ""); - } - - JsoupPageParser parser = new JsoupPageParser(); - ClassInfo info = parser.parseClassPage(document, "org.jsoup.nodes.Attribute"); - assertEquals("org.jsoup.nodes.Attribute", info.getFullName()); - assertEquals(Arrays.asList("public", "class"), info.getModifiers()); - assertFalse(info.isDeprecated()); - assertEquals("http://jsoup.org/apidocs/?org/jsoup/nodes/Attribute.html", info.getUrl()); - assertEquals("A single key + value attribute. Keys are trimmed and normalised to lower-case.", info.getDescription()); - } -} diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.java new file mode 100644 index 0000000..6f5a60a --- /dev/null +++ b/src/test/java/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.java @@ -0,0 +1,87 @@ +package com.gmail.inverseconduit.javadoc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.junit.Test; + +/** + * @author Michael Angstadt + */ +public class LibraryZipFileTest { + private final Path root = Paths.get("src", "test", "resources", "com", "gmail", "inverseconduit", "javadoc"); + private final LibraryZipFile zip; + { + Path file = root.resolve(getClass().getSimpleName() + ".zip"); + try { + zip = new LibraryZipFile(file); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void info_without_file() throws Exception { + Path file = root.resolve(getClass().getSimpleName() + "-no-info.zip"); + LibraryZipFile zip = new LibraryZipFile(file); + assertNull(zip.getName()); + assertNull(zip.getBaseUrl()); + } + + @Test + public void info_without_attributes() throws Exception { + Path file = root.resolve(getClass().getSimpleName() + "-no-attributes.zip"); + LibraryZipFile zip = new LibraryZipFile(file); + assertNull(zip.getName()); + assertNull(zip.getBaseUrl()); + } + + @Test + public void info() { + assertEquals("Java", zip.getName()); + assertEquals("8", zip.getVersion()); + assertEquals("https://docs.oracle.com/javase/8/docs/api/", zip.getBaseUrl()); + assertEquals("http://java.oracle.com", zip.getProjectUrl()); + } + + @Test + public void getClasses() throws Exception { + Iterator it = zip.getClasses(); + Set actual = new HashSet<>(); + while (it.hasNext()) { + actual.add(it.next().getFullyQualified()); + } + + //@formatter:off + Set expected = new HashSet<>(Arrays.asList( + "java.lang.Object", + "java.awt.List", + "java.util.List", + "java.util.Collection" + )); + //@formatter:on + + assertEquals(expected, actual); + } + + @Test + public void getClassInfo_not_found() throws Exception { + ClassInfo info = zip.getClassInfo("java.lang.Foo"); + assertNull(info); + } + + @Test + public void getClassInfo() throws Exception { + ClassInfo info = zip.getClassInfo("java.lang.Object"); + assertEquals("java.lang.Object", info.getName().getFullyQualified()); + assertEquals("Object", info.getName().getSimple()); + } +} diff --git a/src/test/java/com/gmail/inverseconduit/javadoc/ZipPageLoaderTest.java b/src/test/java/com/gmail/inverseconduit/javadoc/ZipPageLoaderTest.java deleted file mode 100644 index 8376f0d..0000000 --- a/src/test/java/com/gmail/inverseconduit/javadoc/ZipPageLoaderTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gmail.inverseconduit.javadoc; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.apache.commons.io.IOUtils; -import org.junit.Test; - -/** - * @author Michael Angstadt - */ -public class ZipPageLoaderTest { - private final Path folder = Paths.get("src", "test", "resources", "com", "gmail", "inverseconduit", "javadoc"); - private final Path file = folder.resolve("javadoc-file.zip"); - private final PageLoader loader; - - { - try { - loader = new ZipPageLoader(file); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Test - public void getAllClassesFiles() throws Exception { - String expected; - try (FileSystem fs = FileSystems.newFileSystem(file, null)) { - Path allClassesFile = fs.getPath("/allclasses-frame.html"); - expected = new String(Files.readAllBytes(allClassesFile)); - } - - String actual; - try (InputStream in = loader.getAllClassesFile()) { - actual = IOUtils.toString(in); - } - - assertEquals(expected, actual); - } - - @Test(expected = IllegalArgumentException.class) - public void getAllClassesFiles_no_allclasses_file() throws Exception { - new ZipPageLoader(folder.resolve("javadoc-without-allclasses-frame.zip")); - } - - @Test(expected = FileSystemNotFoundException.class) - public void getAllClassesFiles_does_not_exist() throws Exception { - new ZipPageLoader(Paths.get("foobar.zip")); - - } - - @Test - public void getClassPage() throws Exception { - String expected; - try (FileSystem fs = FileSystems.newFileSystem(file, null)) { - Path allClassesFile = fs.getPath("/java/lang/String.html"); - expected = new String(Files.readAllBytes(allClassesFile)); - } - - String actual; - try (InputStream in = loader.getClassPage("java.lang.String")) { - actual = IOUtils.toString(in); - } - - assertEquals(expected, actual); - } - - @Test - public void getClassPage_does_not_exist() throws Exception { - InputStream in = loader.getClassPage("java.util.FooBar"); - assertNull(in); - } -} diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/Attribute.html b/src/test/resources/com/gmail/inverseconduit/javadoc/Attribute.html deleted file mode 100644 index 3a051b6..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/Attribute.html +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - -Attribute (jsoup 1.8.1 API) - - - - - - - - - - - - -


- - - - - - - - - - - - - - - - - - - -
- -
- - - -
- -

- -org.jsoup.nodes -
-Class Attribute

-
-java.lang.Object
-  extended by org.jsoup.nodes.Attribute
-
-
-
All Implemented Interfaces:
Cloneable, Map.Entry<String,String>
-
-
-
-
public class Attribute
extends Object
implements Map.Entry<String,String>, Cloneable
- - -

-A single key + value attribute. Keys are trimmed and normalised to lower-case. -

- -

-

-
Author:
-
Jonathan Hedley, jonathan@hedley.net
-
-
- -

- - - - - - - - - - - -
-Constructor Summary
Attribute(String key, - String value) - -
-          Create a new attribute from unencoded (raw) key and value.
-  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Method Summary
- Attributeclone() - -
-           
-static AttributecreateFromEncoded(String unencodedKey, - String encodedValue) - -
-          Create a new Attribute from an unencoded key and a HTML attribute encoded value.
- booleanequals(Object o) - -
-           
- StringgetKey() - -
-          Get the attribute key.
- StringgetValue() - -
-          Get the attribute value.
- inthashCode() - -
-           
- Stringhtml() - -
-          Get the HTML representation of this attribute; e.g.
-protected  voidhtml(StringBuilder accum, - Document.OutputSettings out) - -
-           
-protected  booleanisDataAttribute() - -
-           
- voidsetKey(String key) - -
-          Set the attribute key.
- StringsetValue(String value) - -
-          Set the attribute value.
-protected  booleanshouldCollapseAttribute(Document.OutputSettings out) - -
-          Collapsible if it's a boolean attribute and value is empty or same as name
- StringtoString() - -
-          Get the string representation of this attribute, implemented as html().
- - - - - - - -
Methods inherited from class java.lang.Object
finalize, getClass, notify, notifyAll, wait, wait, wait
-  -

- - - - - - - - -
-Constructor Detail
- -

-Attribute

-
-public Attribute(String key,
-                 String value)
-
-
Create a new attribute from unencoded (raw) key and value. -

-

-
Parameters:
key - attribute key
value - attribute value
See Also:
createFromEncoded(java.lang.String, java.lang.String)
-
- - - - - - - - -
-Method Detail
- -

-getKey

-
-public String getKey()
-
-
Get the attribute key. -

-

-
Specified by:
getKey in interface Map.Entry<String,String>
-
-
- -
Returns:
the attribute key
-
-
-
- -

-setKey

-
-public void setKey(String key)
-
-
Set the attribute key. Gets normalised as per the constructor method. -

-

-
-
-
-
Parameters:
key - the new key; must not be null
-
-
-
- -

-getValue

-
-public String getValue()
-
-
Get the attribute value. -

-

-
Specified by:
getValue in interface Map.Entry<String,String>
-
-
- -
Returns:
the attribute value
-
-
-
- -

-setValue

-
-public String setValue(String value)
-
-
Set the attribute value. -

-

-
Specified by:
setValue in interface Map.Entry<String,String>
-
-
-
Parameters:
value - the new attribute value; must not be null
-
-
-
- -

-html

-
-public String html()
-
-
Get the HTML representation of this attribute; e.g. href="index.html". -

-

-
-
-
- -
Returns:
HTML
-
-
-
- -

-html

-
-protected void html(StringBuilder accum,
-                    Document.OutputSettings out)
-
-
-
-
-
-
-
-
-
- -

-toString

-
-public String toString()
-
-
Get the string representation of this attribute, implemented as html(). -

-

-
Overrides:
toString in class Object
-
-
- -
Returns:
string
-
-
-
- -

-createFromEncoded

-
-public static Attribute createFromEncoded(String unencodedKey,
-                                          String encodedValue)
-
-
Create a new Attribute from an unencoded key and a HTML attribute encoded value. -

-

-
-
-
-
Parameters:
unencodedKey - assumes the key is not encoded, as can be only run of simple \w chars.
encodedValue - HTML attribute encoded value -
Returns:
attribute
-
-
-
- -

-isDataAttribute

-
-protected boolean isDataAttribute()
-
-
-
-
-
-
-
-
-
- -

-shouldCollapseAttribute

-
-protected final boolean shouldCollapseAttribute(Document.OutputSettings out)
-
-
Collapsible if it's a boolean attribute and value is empty or same as name -

-

-
-
-
-
-
-
-
- -

-equals

-
-public boolean equals(Object o)
-
-
-
Specified by:
equals in interface Map.Entry<String,String>
Overrides:
equals in class Object
-
-
-
-
-
-
- -

-hashCode

-
-public int hashCode()
-
-
-
Specified by:
hashCode in interface Map.Entry<String,String>
Overrides:
hashCode in class Object
-
-
-
-
-
-
- -

-clone

-
-public Attribute clone()
-
-
-
Overrides:
clone in class Object
-
-
-
-
-
- -
- - - - - - - - - - - - - - - - - - - -
- -
- - - -
-Copyright © 2009-2014 Jonathan Hedley. All Rights Reserved. - - diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-attributes.zip b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-attributes.zip new file mode 100644 index 0000000..becda5e Binary files /dev/null and b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-attributes.zip differ diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-info.zip b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-info.zip new file mode 100644 index 0000000..23a683b Binary files /dev/null and b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest-no-info.zip differ diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.zip b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.zip new file mode 100644 index 0000000..4bbdebb Binary files /dev/null and b/src/test/resources/com/gmail/inverseconduit/javadoc/LibraryZipFileTest.zip differ diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/String.html b/src/test/resources/com/gmail/inverseconduit/javadoc/String.html deleted file mode 100644 index 1408e45..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/String.html +++ /dev/null @@ -1,3573 +0,0 @@ - - - - - -String (Java Platform SE 8 ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - - -
-
compact1, compact2, compact3
-
java.lang
-

Class String

-
-
- -
- -
-
-
    -
  • - -
      -
    • - - -

      Field Summary

      - - - - - - - - - - -
      Fields 
      Modifier and TypeField and Description
      static Comparator<String>CASE_INSENSITIVE_ORDER -
      A Comparator that orders String objects as by - compareToIgnoreCase.
      -
      -
    • -
    - -
      -
    • - - -

      Constructor Summary

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Constructors 
      Constructor and Description
      String() -
      Initializes a newly created String object so that it represents - an empty character sequence.
      -
      String(byte[] bytes) -
      Constructs a new String by decoding the specified array of bytes - using the platform's default charset.
      -
      String(byte[] bytes, - Charset charset) -
      Constructs a new String by decoding the specified array of - bytes using the specified charset.
      -
      String(byte[] ascii, - int hibyte) -
      Deprecated.  -
      This method does not properly convert bytes into - characters. As of JDK 1.1, the preferred way to do this is via the - String constructors that take a Charset, charset name, or that use the platform's - default charset.
      -
      -
      String(byte[] bytes, - int offset, - int length) -
      Constructs a new String by decoding the specified subarray of - bytes using the platform's default charset.
      -
      String(byte[] bytes, - int offset, - int length, - Charset charset) -
      Constructs a new String by decoding the specified subarray of - bytes using the specified charset.
      -
      String(byte[] ascii, - int hibyte, - int offset, - int count) -
      Deprecated.  -
      This method does not properly convert bytes into characters. - As of JDK 1.1, the preferred way to do this is via the - String constructors that take a Charset, charset name, or that use the platform's - default charset.
      -
      -
      String(byte[] bytes, - int offset, - int length, - String charsetName) -
      Constructs a new String by decoding the specified subarray of - bytes using the specified charset.
      -
      String(byte[] bytes, - String charsetName) -
      Constructs a new String by decoding the specified array of bytes - using the specified charset.
      -
      String(char[] value) -
      Allocates a new String so that it represents the sequence of - characters currently contained in the character array argument.
      -
      String(char[] value, - int offset, - int count) -
      Allocates a new String that contains characters from a subarray - of the character array argument.
      -
      String(int[] codePoints, - int offset, - int count) -
      Allocates a new String that contains characters from a subarray - of the Unicode code point array - argument.
      -
      String(String original) -
      Initializes a newly created String object so that it represents - the same sequence of characters as the argument; in other words, the - newly created string is a copy of the argument string.
      -
      String(StringBuffer buffer) -
      Allocates a new string that contains the sequence of characters - currently contained in the string buffer argument.
      -
      String(StringBuilder builder) -
      Allocates a new string that contains the sequence of characters - currently contained in the string builder argument.
      -
      -
    • -
    - -
      -
    • - - -

      Method Summary

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      All Methods Static Methods Instance Methods Concrete Methods Deprecated Methods 
      Modifier and TypeMethod and Description
      charcharAt(int index) -
      Returns the char value at the - specified index.
      -
      intcodePointAt(int index) -
      Returns the character (Unicode code point) at the specified - index.
      -
      intcodePointBefore(int index) -
      Returns the character (Unicode code point) before the specified - index.
      -
      intcodePointCount(int beginIndex, - int endIndex) -
      Returns the number of Unicode code points in the specified text - range of this String.
      -
      intcompareTo(String anotherString) -
      Compares two strings lexicographically.
      -
      intcompareToIgnoreCase(String str) -
      Compares two strings lexicographically, ignoring case - differences.
      -
      Stringconcat(String str) -
      Concatenates the specified string to the end of this string.
      -
      booleancontains(CharSequence s) -
      Returns true if and only if this string contains the specified - sequence of char values.
      -
      booleancontentEquals(CharSequence cs) -
      Compares this string to the specified CharSequence.
      -
      booleancontentEquals(StringBuffer sb) -
      Compares this string to the specified StringBuffer.
      -
      static StringcopyValueOf(char[] data) -
      Equivalent to valueOf(char[]).
      -
      static StringcopyValueOf(char[] data, - int offset, - int count) - -
      booleanendsWith(String suffix) -
      Tests if this string ends with the specified suffix.
      -
      booleanequals(Object anObject) -
      Compares this string to the specified object.
      -
      booleanequalsIgnoreCase(String anotherString) -
      Compares this String to another String, ignoring case - considerations.
      -
      static Stringformat(Locale l, - String format, - Object... args) -
      Returns a formatted string using the specified locale, format string, - and arguments.
      -
      static Stringformat(String format, - Object... args) -
      Returns a formatted string using the specified format string and - arguments.
      -
      byte[]getBytes() -
      Encodes this String into a sequence of bytes using the - platform's default charset, storing the result into a new byte array.
      -
      byte[]getBytes(Charset charset) -
      Encodes this String into a sequence of bytes using the given - charset, storing the result into a - new byte array.
      -
      voidgetBytes(int srcBegin, - int srcEnd, - byte[] dst, - int dstBegin) -
      Deprecated.  -
      This method does not properly convert characters into - bytes. As of JDK 1.1, the preferred way to do this is via the - getBytes() method, which uses the platform's default charset.
      -
      -
      byte[]getBytes(String charsetName) -
      Encodes this String into a sequence of bytes using the named - charset, storing the result into a new byte array.
      -
      voidgetChars(int srcBegin, - int srcEnd, - char[] dst, - int dstBegin) -
      Copies characters from this string into the destination character - array.
      -
      inthashCode() -
      Returns a hash code for this string.
      -
      intindexOf(int ch) -
      Returns the index within this string of the first occurrence of - the specified character.
      -
      intindexOf(int ch, - int fromIndex) -
      Returns the index within this string of the first occurrence of the - specified character, starting the search at the specified index.
      -
      intindexOf(String str) -
      Returns the index within this string of the first occurrence of the - specified substring.
      -
      intindexOf(String str, - int fromIndex) -
      Returns the index within this string of the first occurrence of the - specified substring, starting at the specified index.
      -
      Stringintern() -
      Returns a canonical representation for the string object.
      -
      booleanisEmpty() -
      Returns true if, and only if, length() is 0.
      -
      static Stringjoin(CharSequence delimiter, - CharSequence... elements) -
      Returns a new String composed of copies of the - CharSequence elements joined together with a copy of - the specified delimiter.
      -
      static Stringjoin(CharSequence delimiter, - Iterable<? extends CharSequence> elements) -
      Returns a new String composed of copies of the - CharSequence elements joined together with a copy of the - specified delimiter.
      -
      intlastIndexOf(int ch) -
      Returns the index within this string of the last occurrence of - the specified character.
      -
      intlastIndexOf(int ch, - int fromIndex) -
      Returns the index within this string of the last occurrence of - the specified character, searching backward starting at the - specified index.
      -
      intlastIndexOf(String str) -
      Returns the index within this string of the last occurrence of the - specified substring.
      -
      intlastIndexOf(String str, - int fromIndex) -
      Returns the index within this string of the last occurrence of the - specified substring, searching backward starting at the specified index.
      -
      intlength() -
      Returns the length of this string.
      -
      booleanmatches(String regex) -
      Tells whether or not this string matches the given regular expression.
      -
      intoffsetByCodePoints(int index, - int codePointOffset) -
      Returns the index within this String that is - offset from the given index by - codePointOffset code points.
      -
      booleanregionMatches(boolean ignoreCase, - int toffset, - String other, - int ooffset, - int len) -
      Tests if two string regions are equal.
      -
      booleanregionMatches(int toffset, - String other, - int ooffset, - int len) -
      Tests if two string regions are equal.
      -
      Stringreplace(char oldChar, - char newChar) -
      Returns a string resulting from replacing all occurrences of - oldChar in this string with newChar.
      -
      Stringreplace(CharSequence target, - CharSequence replacement) -
      Replaces each substring of this string that matches the literal target - sequence with the specified literal replacement sequence.
      -
      StringreplaceAll(String regex, - String replacement) -
      Replaces each substring of this string that matches the given regular expression with the - given replacement.
      -
      StringreplaceFirst(String regex, - String replacement) -
      Replaces the first substring of this string that matches the given regular expression with the - given replacement.
      -
      String[]split(String regex) -
      Splits this string around matches of the given regular expression.
      -
      String[]split(String regex, - int limit) -
      Splits this string around matches of the given - regular expression.
      -
      booleanstartsWith(String prefix) -
      Tests if this string starts with the specified prefix.
      -
      booleanstartsWith(String prefix, - int toffset) -
      Tests if the substring of this string beginning at the - specified index starts with the specified prefix.
      -
      CharSequencesubSequence(int beginIndex, - int endIndex) -
      Returns a character sequence that is a subsequence of this sequence.
      -
      Stringsubstring(int beginIndex) -
      Returns a string that is a substring of this string.
      -
      Stringsubstring(int beginIndex, - int endIndex) -
      Returns a string that is a substring of this string.
      -
      char[]toCharArray() -
      Converts this string to a new character array.
      -
      StringtoLowerCase() -
      Converts all of the characters in this String to lower - case using the rules of the default locale.
      -
      StringtoLowerCase(Locale locale) -
      Converts all of the characters in this String to lower - case using the rules of the given Locale.
      -
      StringtoString() -
      This object (which is already a string!) is itself returned.
      -
      StringtoUpperCase() -
      Converts all of the characters in this String to upper - case using the rules of the default locale.
      -
      StringtoUpperCase(Locale locale) -
      Converts all of the characters in this String to upper - case using the rules of the given Locale.
      -
      Stringtrim() -
      Returns a string whose value is this string, with any leading and trailing - whitespace removed.
      -
      static StringvalueOf(boolean b) -
      Returns the string representation of the boolean argument.
      -
      static StringvalueOf(char c) -
      Returns the string representation of the char - argument.
      -
      static StringvalueOf(char[] data) -
      Returns the string representation of the char array - argument.
      -
      static StringvalueOf(char[] data, - int offset, - int count) -
      Returns the string representation of a specific subarray of the - char array argument.
      -
      static StringvalueOf(double d) -
      Returns the string representation of the double argument.
      -
      static StringvalueOf(float f) -
      Returns the string representation of the float argument.
      -
      static StringvalueOf(int i) -
      Returns the string representation of the int argument.
      -
      static StringvalueOf(long l) -
      Returns the string representation of the long argument.
      -
      static StringvalueOf(Object obj) -
      Returns the string representation of the Object argument.
      -
      - - -
    • -
    -
  • -
-
-
-
    -
  • - -
      -
    • - - -

      Field Detail

      - - - -
        -
      • -

        CASE_INSENSITIVE_ORDER

        -
        public static final Comparator<String> CASE_INSENSITIVE_ORDER
        -
        A Comparator that orders String objects as by - compareToIgnoreCase. This comparator is serializable. -

        - Note that this Comparator does not take locale into account, - and will result in an unsatisfactory ordering for certain locales. - The java.text package provides Collators to allow - locale-sensitive ordering.

        -
        -
        Since:
        -
        1.2
        -
        See Also:
        -
        Collator.compare(String, String)
        -
        -
      • -
      -
    • -
    - -
      -
    • - - -

      Constructor Detail

      - - - -
        -
      • -

        String

        -
        public String()
        -
        Initializes a newly created String object so that it represents - an empty character sequence. Note that use of this constructor is - unnecessary since Strings are immutable.
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(String original)
        -
        Initializes a newly created String object so that it represents - the same sequence of characters as the argument; in other words, the - newly created string is a copy of the argument string. Unless an - explicit copy of original is needed, use of this constructor is - unnecessary since Strings are immutable.
        -
        -
        Parameters:
        -
        original - A String
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(char[] value)
        -
        Allocates a new String so that it represents the sequence of - characters currently contained in the character array argument. The - contents of the character array are copied; subsequent modification of - the character array does not affect the newly created string.
        -
        -
        Parameters:
        -
        value - The initial value of the string
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(char[] value,
        -              int offset,
        -              int count)
        -
        Allocates a new String that contains characters from a subarray - of the character array argument. The offset argument is the - index of the first character of the subarray and the count - argument specifies the length of the subarray. The contents of the - subarray are copied; subsequent modification of the character array does - not affect the newly created string.
        -
        -
        Parameters:
        -
        value - Array that is the source of characters
        -
        offset - The initial offset
        -
        count - The length
        -
        Throws:
        -
        IndexOutOfBoundsException - If the offset and count arguments index - characters outside the bounds of the value array
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(int[] codePoints,
        -              int offset,
        -              int count)
        -
        Allocates a new String that contains characters from a subarray - of the Unicode code point array - argument. The offset argument is the index of the first code - point of the subarray and the count argument specifies the - length of the subarray. The contents of the subarray are converted to - chars; subsequent modification of the int array does not - affect the newly created string.
        -
        -
        Parameters:
        -
        codePoints - Array that is the source of Unicode code points
        -
        offset - The initial offset
        -
        count - The length
        -
        Throws:
        -
        IllegalArgumentException - If any invalid Unicode code point is found in codePoints
        -
        IndexOutOfBoundsException - If the offset and count arguments index - characters outside the bounds of the codePoints array
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - - - - - - - - - -
        -
      • -

        String

        -
        public String(byte[] bytes,
        -              int offset,
        -              int length,
        -              String charsetName)
        -       throws UnsupportedEncodingException
        -
        Constructs a new String by decoding the specified subarray of - bytes using the specified charset. The length of the new String - is a function of the charset, and hence may not be equal to the length - of the subarray. - -

        The behavior of this constructor when the given bytes are not valid - in the given charset is unspecified. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        offset - The index of the first byte to decode
        -
        length - The number of bytes to decode
        -
        charsetName - The name of a supported charset
        -
        Throws:
        -
        UnsupportedEncodingException - If the named charset is not supported
        -
        IndexOutOfBoundsException - If the offset and length arguments index - characters outside the bounds of the bytes array
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(byte[] bytes,
        -              int offset,
        -              int length,
        -              Charset charset)
        -
        Constructs a new String by decoding the specified subarray of - bytes using the specified charset. - The length of the new String is a function of the charset, and - hence may not be equal to the length of the subarray. - -

        This method always replaces malformed-input and unmappable-character - sequences with this charset's default replacement string. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        offset - The index of the first byte to decode
        -
        length - The number of bytes to decode
        -
        charset - The charset to be used to - decode the bytes
        -
        Throws:
        -
        IndexOutOfBoundsException - If the offset and length arguments index - characters outside the bounds of the bytes array
        -
        Since:
        -
        1.6
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(byte[] bytes,
        -              String charsetName)
        -       throws UnsupportedEncodingException
        -
        Constructs a new String by decoding the specified array of bytes - using the specified charset. The - length of the new String is a function of the charset, and hence - may not be equal to the length of the byte array. - -

        The behavior of this constructor when the given bytes are not valid - in the given charset is unspecified. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        charsetName - The name of a supported charset
        -
        Throws:
        -
        UnsupportedEncodingException - If the named charset is not supported
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(byte[] bytes,
        -              Charset charset)
        -
        Constructs a new String by decoding the specified array of - bytes using the specified charset. - The length of the new String is a function of the charset, and - hence may not be equal to the length of the byte array. - -

        This method always replaces malformed-input and unmappable-character - sequences with this charset's default replacement string. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        charset - The charset to be used to - decode the bytes
        -
        Since:
        -
        1.6
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(byte[] bytes,
        -              int offset,
        -              int length)
        -
        Constructs a new String by decoding the specified subarray of - bytes using the platform's default charset. The length of the new - String is a function of the charset, and hence may not be equal - to the length of the subarray. - -

        The behavior of this constructor when the given bytes are not valid - in the default charset is unspecified. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        offset - The index of the first byte to decode
        -
        length - The number of bytes to decode
        -
        Throws:
        -
        IndexOutOfBoundsException - If the offset and the length arguments index - characters outside the bounds of the bytes array
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(byte[] bytes)
        -
        Constructs a new String by decoding the specified array of bytes - using the platform's default charset. The length of the new String is a function of the charset, and hence may not be equal to the - length of the byte array. - -

        The behavior of this constructor when the given bytes are not valid - in the default charset is unspecified. The CharsetDecoder class should be used when more control - over the decoding process is required.

        -
        -
        Parameters:
        -
        bytes - The bytes to be decoded into characters
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(StringBuffer buffer)
        -
        Allocates a new string that contains the sequence of characters - currently contained in the string buffer argument. The contents of the - string buffer are copied; subsequent modification of the string buffer - does not affect the newly created string.
        -
        -
        Parameters:
        -
        buffer - A StringBuffer
        -
        -
      • -
      - - - -
        -
      • -

        String

        -
        public String(StringBuilder builder)
        -
        Allocates a new string that contains the sequence of characters - currently contained in the string builder argument. The contents of the - string builder are copied; subsequent modification of the string builder - does not affect the newly created string. - -

        This constructor is provided to ease migration to StringBuilder. Obtaining a string from a string builder via the toString method is likely to run faster and is generally preferred.

        -
        -
        Parameters:
        -
        builder - A StringBuilder
        -
        Since:
        -
        1.5
        -
        -
      • -
      -
    • -
    - -
      -
    • - - -

      Method Detail

      - - - -
        -
      • -

        length

        -
        public int length()
        -
        Returns the length of this string. - The length is equal to the number of Unicode - code units in the string.
        -
        -
        Specified by:
        -
        length in interface CharSequence
        -
        Returns:
        -
        the length of the sequence of characters represented by this - object.
        -
        -
      • -
      - - - -
        -
      • -

        isEmpty

        -
        public boolean isEmpty()
        -
        Returns true if, and only if, length() is 0.
        -
        -
        Returns:
        -
        true if length() is 0, otherwise - false
        -
        Since:
        -
        1.6
        -
        -
      • -
      - - - -
        -
      • -

        charAt

        -
        public char charAt(int index)
        -
        Returns the char value at the - specified index. An index ranges from 0 to - length() - 1. The first char value of the sequence - is at index 0, the next at index 1, - and so on, as for array indexing. - -

        If the char value specified by the index is a - surrogate, the surrogate - value is returned.

        -
        -
        Specified by:
        -
        charAt in interface CharSequence
        -
        Parameters:
        -
        index - the index of the char value.
        -
        Returns:
        -
        the char value at the specified index of this string. - The first char value is at index 0.
        -
        Throws:
        -
        IndexOutOfBoundsException - if the index - argument is negative or not less than the length of this - string.
        -
        -
      • -
      - - - -
        -
      • -

        codePointAt

        -
        public int codePointAt(int index)
        -
        Returns the character (Unicode code point) at the specified - index. The index refers to char values - (Unicode code units) and ranges from 0 to - length()- 1. - -

        If the char value specified at the given index - is in the high-surrogate range, the following index is less - than the length of this String, and the - char value at the following index is in the - low-surrogate range, then the supplementary code point - corresponding to this surrogate pair is returned. Otherwise, - the char value at the given index is returned.

        -
        -
        Parameters:
        -
        index - the index to the char values
        -
        Returns:
        -
        the code point value of the character at the - index
        -
        Throws:
        -
        IndexOutOfBoundsException - if the index - argument is negative or not less than the length of this - string.
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        codePointBefore

        -
        public int codePointBefore(int index)
        -
        Returns the character (Unicode code point) before the specified - index. The index refers to char values - (Unicode code units) and ranges from 1 to length. - -

        If the char value at (index - 1) - is in the low-surrogate range, (index - 2) is not - negative, and the char value at (index - - 2) is in the high-surrogate range, then the - supplementary code point value of the surrogate pair is - returned. If the char value at index - - 1 is an unpaired low-surrogate or a high-surrogate, the - surrogate value is returned.

        -
        -
        Parameters:
        -
        index - the index following the code point that should be returned
        -
        Returns:
        -
        the Unicode code point value before the given index.
        -
        Throws:
        -
        IndexOutOfBoundsException - if the index - argument is less than 1 or greater than the length - of this string.
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        codePointCount

        -
        public int codePointCount(int beginIndex,
        -                          int endIndex)
        -
        Returns the number of Unicode code points in the specified text - range of this String. The text range begins at the - specified beginIndex and extends to the - char at index endIndex - 1. Thus the - length (in chars) of the text range is - endIndex-beginIndex. Unpaired surrogates within - the text range count as one code point each.
        -
        -
        Parameters:
        -
        beginIndex - the index to the first char of - the text range.
        -
        endIndex - the index after the last char of - the text range.
        -
        Returns:
        -
        the number of Unicode code points in the specified text - range
        -
        Throws:
        -
        IndexOutOfBoundsException - if the - beginIndex is negative, or endIndex - is larger than the length of this String, or - beginIndex is larger than endIndex.
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        offsetByCodePoints

        -
        public int offsetByCodePoints(int index,
        -                              int codePointOffset)
        -
        Returns the index within this String that is - offset from the given index by - codePointOffset code points. Unpaired surrogates - within the text range given by index and - codePointOffset count as one code point each.
        -
        -
        Parameters:
        -
        index - the index to be offset
        -
        codePointOffset - the offset in code points
        -
        Returns:
        -
        the index within this String
        -
        Throws:
        -
        IndexOutOfBoundsException - if index - is negative or larger then the length of this - String, or if codePointOffset is positive - and the substring starting with index has fewer - than codePointOffset code points, - or if codePointOffset is negative and the substring - before index has fewer than the absolute value - of codePointOffset code points.
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        getChars

        -
        public void getChars(int srcBegin,
        -                     int srcEnd,
        -                     char[] dst,
        -                     int dstBegin)
        -
        Copies characters from this string into the destination character - array. -

        - The first character to be copied is at index srcBegin; - the last character to be copied is at index srcEnd-1 - (thus the total number of characters to be copied is - srcEnd-srcBegin). The characters are copied into the - subarray of dst starting at index dstBegin - and ending at index: -

        -     dstbegin + (srcEnd-srcBegin) - 1
        - 
        -
        -
        Parameters:
        -
        srcBegin - index of the first character in the string - to copy.
        -
        srcEnd - index after the last character in the string - to copy.
        -
        dst - the destination array.
        -
        dstBegin - the start offset in the destination array.
        -
        Throws:
        -
        IndexOutOfBoundsException - If any of the following - is true: -
        • srcBegin is negative. -
        • srcBegin is greater than srcEnd -
        • srcEnd is greater than the length of this - string -
        • dstBegin is negative -
        • dstBegin+(srcEnd-srcBegin) is larger than - dst.length
        -
        -
      • -
      - - - -
        -
      • -

        getBytes

        -
        @Deprecated
        -public void getBytes(int srcBegin,
        -                                 int srcEnd,
        -                                 byte[] dst,
        -                                 int dstBegin)
        -
        Deprecated. This method does not properly convert characters into - bytes. As of JDK 1.1, the preferred way to do this is via the - getBytes() method, which uses the platform's default charset.
        -
        Copies characters from this string into the destination byte array. Each - byte receives the 8 low-order bits of the corresponding character. The - eight high-order bits of each character are not copied and do not - participate in the transfer in any way. - -

        The first character to be copied is at index srcBegin; the - last character to be copied is at index srcEnd-1. The total - number of characters to be copied is srcEnd-srcBegin. The - characters, converted to bytes, are copied into the subarray of dst starting at index dstBegin and ending at index: - -

        -     dstbegin + (srcEnd-srcBegin) - 1
        - 
        -
        -
        Parameters:
        -
        srcBegin - Index of the first character in the string to copy
        -
        srcEnd - Index after the last character in the string to copy
        -
        dst - The destination array
        -
        dstBegin - The start offset in the destination array
        -
        Throws:
        -
        IndexOutOfBoundsException - If any of the following is true: -
          -
        • srcBegin is negative -
        • srcBegin is greater than srcEnd -
        • srcEnd is greater than the length of this String -
        • dstBegin is negative -
        • dstBegin+(srcEnd-srcBegin) is larger than dst.length -
        -
        -
      • -
      - - - -
        -
      • -

        getBytes

        -
        public byte[] getBytes(String charsetName)
        -                throws UnsupportedEncodingException
        -
        Encodes this String into a sequence of bytes using the named - charset, storing the result into a new byte array. - -

        The behavior of this method when this string cannot be encoded in - the given charset is unspecified. The CharsetEncoder class should be used when more control - over the encoding process is required.

        -
        -
        Parameters:
        -
        charsetName - The name of a supported charset
        -
        Returns:
        -
        The resultant byte array
        -
        Throws:
        -
        UnsupportedEncodingException - If the named charset is not supported
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        getBytes

        -
        public byte[] getBytes(Charset charset)
        -
        Encodes this String into a sequence of bytes using the given - charset, storing the result into a - new byte array. - -

        This method always replaces malformed-input and unmappable-character - sequences with this charset's default replacement byte array. The - CharsetEncoder class should be used when more - control over the encoding process is required.

        -
        -
        Parameters:
        -
        charset - The Charset to be used to encode - the String
        -
        Returns:
        -
        The resultant byte array
        -
        Since:
        -
        1.6
        -
        -
      • -
      - - - -
        -
      • -

        getBytes

        -
        public byte[] getBytes()
        -
        Encodes this String into a sequence of bytes using the - platform's default charset, storing the result into a new byte array. - -

        The behavior of this method when this string cannot be encoded in - the default charset is unspecified. The CharsetEncoder class should be used when more control - over the encoding process is required.

        -
        -
        Returns:
        -
        The resultant byte array
        -
        Since:
        -
        JDK1.1
        -
        -
      • -
      - - - -
        -
      • -

        equals

        -
        public boolean equals(Object anObject)
        -
        Compares this string to the specified object. The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this - object.
        -
        -
        Overrides:
        -
        equals in class Object
        -
        Parameters:
        -
        anObject - The object to compare this String against
        -
        Returns:
        -
        true if the given object represents a String - equivalent to this string, false otherwise
        -
        See Also:
        -
        compareTo(String), -equalsIgnoreCase(String)
        -
        -
      • -
      - - - -
        -
      • -

        contentEquals

        -
        public boolean contentEquals(StringBuffer sb)
        -
        Compares this string to the specified StringBuffer. The result - is true if and only if this String represents the same - sequence of characters as the specified StringBuffer. This method - synchronizes on the StringBuffer.
        -
        -
        Parameters:
        -
        sb - The StringBuffer to compare this String against
        -
        Returns:
        -
        true if this String represents the same - sequence of characters as the specified StringBuffer, - false otherwise
        -
        Since:
        -
        1.4
        -
        -
      • -
      - - - -
        -
      • -

        contentEquals

        -
        public boolean contentEquals(CharSequence cs)
        -
        Compares this string to the specified CharSequence. The - result is true if and only if this String represents the - same sequence of char values as the specified sequence. Note that if the - CharSequence is a StringBuffer then the method - synchronizes on it.
        -
        -
        Parameters:
        -
        cs - The sequence to compare this String against
        -
        Returns:
        -
        true if this String represents the same - sequence of char values as the specified sequence, false otherwise
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        equalsIgnoreCase

        -
        public boolean equalsIgnoreCase(String anotherString)
        -
        Compares this String to another String, ignoring case - considerations. Two strings are considered equal ignoring case if they - are of the same length and corresponding characters in the two strings - are equal ignoring case. - -

        Two characters c1 and c2 are considered the same - ignoring case if at least one of the following is true: -

        -
        -
        Parameters:
        -
        anotherString - The String to compare this String against
        -
        Returns:
        -
        true if the argument is not null and it - represents an equivalent String ignoring case; false otherwise
        -
        See Also:
        -
        equals(Object)
        -
        -
      • -
      - - - -
        -
      • -

        compareTo

        -
        public int compareTo(String anotherString)
        -
        Compares two strings lexicographically. - The comparison is based on the Unicode value of each character in - the strings. The character sequence represented by this - String object is compared lexicographically to the - character sequence represented by the argument string. The result is - a negative integer if this String object - lexicographically precedes the argument string. The result is a - positive integer if this String object lexicographically - follows the argument string. The result is zero if the strings - are equal; compareTo returns 0 exactly when - the equals(Object) method would return true. -

        - This is the definition of lexicographic ordering. If two strings are - different, then either they have different characters at some index - that is a valid index for both strings, or their lengths are different, - or both. If they have different characters at one or more index - positions, let k be the smallest such index; then the string - whose character at position k has the smaller value, as - determined by using the < operator, lexicographically precedes the - other string. In this case, compareTo returns the - difference of the two character values at position k in - the two string -- that is, the value: -

        - this.charAt(k)-anotherString.charAt(k)
        - 
        - If there is no index position at which they differ, then the shorter - string lexicographically precedes the longer string. In this case, - compareTo returns the difference of the lengths of the - strings -- that is, the value: -
        - this.length()-anotherString.length()
        - 
        -
        -
        Specified by:
        -
        compareTo in interface Comparable<String>
        -
        Parameters:
        -
        anotherString - the String to be compared.
        -
        Returns:
        -
        the value 0 if the argument string is equal to - this string; a value less than 0 if this string - is lexicographically less than the string argument; and a - value greater than 0 if this string is - lexicographically greater than the string argument.
        -
        -
      • -
      - - - -
        -
      • -

        compareToIgnoreCase

        -
        public int compareToIgnoreCase(String str)
        -
        Compares two strings lexicographically, ignoring case - differences. This method returns an integer whose sign is that of - calling compareTo with normalized versions of the strings - where case differences have been eliminated by calling - Character.toLowerCase(Character.toUpperCase(character)) on - each character. -

        - Note that this method does not take locale into account, - and will result in an unsatisfactory ordering for certain locales. - The java.text package provides collators to allow - locale-sensitive ordering.

        -
        -
        Parameters:
        -
        str - the String to be compared.
        -
        Returns:
        -
        a negative integer, zero, or a positive integer as the - specified String is greater than, equal to, or less - than this String, ignoring case considerations.
        -
        Since:
        -
        1.2
        -
        See Also:
        -
        Collator.compare(String, String)
        -
        -
      • -
      - - - -
        -
      • -

        regionMatches

        -
        public boolean regionMatches(int toffset,
        -                             String other,
        -                             int ooffset,
        -                             int len)
        -
        Tests if two string regions are equal. -

        - A substring of this String object is compared to a substring - of the argument other. The result is true if these substrings - represent identical character sequences. The substring of this - String object to be compared begins at index toffset - and has length len. The substring of other to be compared - begins at index ooffset and has length len. The - result is false if and only if at least one of the following - is true: -

        • toffset is negative. -
        • ooffset is negative. -
        • toffset+len is greater than the length of this - String object. -
        • ooffset+len is greater than the length of the other - argument. -
        • There is some nonnegative integer k less than len - such that: - this.charAt(toffset + k) != other.charAt(ooffset + - k) -
        -
        -
        Parameters:
        -
        toffset - the starting offset of the subregion in this string.
        -
        other - the string argument.
        -
        ooffset - the starting offset of the subregion in the string - argument.
        -
        len - the number of characters to compare.
        -
        Returns:
        -
        true if the specified subregion of this string - exactly matches the specified subregion of the string argument; - false otherwise.
        -
        -
      • -
      - - - -
        -
      • -

        regionMatches

        -
        public boolean regionMatches(boolean ignoreCase,
        -                             int toffset,
        -                             String other,
        -                             int ooffset,
        -                             int len)
        -
        Tests if two string regions are equal. -

        - A substring of this String object is compared to a substring - of the argument other. The result is true if these - substrings represent character sequences that are the same, ignoring - case if and only if ignoreCase is true. The substring of - this String object to be compared begins at index - toffset and has length len. The substring of - other to be compared begins at index ooffset and - has length len. The result is false if and only if - at least one of the following is true: -

        • toffset is negative. -
        • ooffset is negative. -
        • toffset+len is greater than the length of this - String object. -
        • ooffset+len is greater than the length of the other - argument. -
        • ignoreCase is false and there is some nonnegative - integer k less than len such that: -
          - this.charAt(toffset+k) != other.charAt(ooffset+k)
          - 
          -
        • ignoreCase is true and there is some nonnegative - integer k less than len such that: -
          - Character.toLowerCase(this.charAt(toffset+k)) !=
          -     Character.toLowerCase(other.charAt(ooffset+k))
          - 
          - and: -
          - Character.toUpperCase(this.charAt(toffset+k)) !=
          -         Character.toUpperCase(other.charAt(ooffset+k))
          - 
          -
        -
        -
        Parameters:
        -
        ignoreCase - if true, ignore case when comparing - characters.
        -
        toffset - the starting offset of the subregion in this - string.
        -
        other - the string argument.
        -
        ooffset - the starting offset of the subregion in the string - argument.
        -
        len - the number of characters to compare.
        -
        Returns:
        -
        true if the specified subregion of this string - matches the specified subregion of the string argument; - false otherwise. Whether the matching is exact - or case insensitive depends on the ignoreCase - argument.
        -
        -
      • -
      - - - -
        -
      • -

        startsWith

        -
        public boolean startsWith(String prefix,
        -                          int toffset)
        -
        Tests if the substring of this string beginning at the - specified index starts with the specified prefix.
        -
        -
        Parameters:
        -
        prefix - the prefix.
        -
        toffset - where to begin looking in this string.
        -
        Returns:
        -
        true if the character sequence represented by the - argument is a prefix of the substring of this object starting - at index toffset; false otherwise. - The result is false if toffset is - negative or greater than the length of this - String object; otherwise the result is the same - as the result of the expression -
        -          this.substring(toffset).startsWith(prefix)
        -          
        -
        -
      • -
      - - - -
        -
      • -

        startsWith

        -
        public boolean startsWith(String prefix)
        -
        Tests if this string starts with the specified prefix.
        -
        -
        Parameters:
        -
        prefix - the prefix.
        -
        Returns:
        -
        true if the character sequence represented by the - argument is a prefix of the character sequence represented by - this string; false otherwise. - Note also that true will be returned if the - argument is an empty string or is equal to this - String object as determined by the - equals(Object) method.
        -
        Since:
        -
        1. 0
        -
        -
      • -
      - - - -
        -
      • -

        endsWith

        -
        public boolean endsWith(String suffix)
        -
        Tests if this string ends with the specified suffix.
        -
        -
        Parameters:
        -
        suffix - the suffix.
        -
        Returns:
        -
        true if the character sequence represented by the - argument is a suffix of the character sequence represented by - this object; false otherwise. Note that the - result will be true if the argument is the - empty string or is equal to this String object - as determined by the equals(Object) method.
        -
        -
      • -
      - - - -
        -
      • -

        hashCode

        -
        public int hashCode()
        -
        Returns a hash code for this string. The hash code for a - String object is computed as -
        - s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
        - 
        - using int arithmetic, where s[i] is the - ith character of the string, n is the length of - the string, and ^ indicates exponentiation. - (The hash value of the empty string is zero.)
        -
        -
        Overrides:
        -
        hashCode in class Object
        -
        Returns:
        -
        a hash code value for this object.
        -
        See Also:
        -
        Object.equals(java.lang.Object), -System.identityHashCode(java.lang.Object)
        -
        -
      • -
      - - - -
        -
      • -

        indexOf

        -
        public int indexOf(int ch)
        -
        Returns the index within this string of the first occurrence of - the specified character. If a character with value - ch occurs in the character sequence represented by - this String object, then the index (in Unicode - code units) of the first such occurrence is returned. For - values of ch in the range from 0 to 0xFFFF - (inclusive), this is the smallest value k such that: -
        - this.charAt(k) == ch
        - 
        - is true. For other values of ch, it is the - smallest value k such that: -
        - this.codePointAt(k) == ch
        - 
        - is true. In either case, if no such character occurs in this - string, then -1 is returned.
        -
        -
        Parameters:
        -
        ch - a character (Unicode code point).
        -
        Returns:
        -
        the index of the first occurrence of the character in the - character sequence represented by this object, or - -1 if the character does not occur.
        -
        -
      • -
      - - - -
        -
      • -

        indexOf

        -
        public int indexOf(int ch,
        -                   int fromIndex)
        -
        Returns the index within this string of the first occurrence of the - specified character, starting the search at the specified index. -

        - If a character with value ch occurs in the - character sequence represented by this String - object at an index no smaller than fromIndex, then - the index of the first such occurrence is returned. For values - of ch in the range from 0 to 0xFFFF (inclusive), - this is the smallest value k such that: -

        - (this.charAt(k) == ch)  && (k >= fromIndex)
        - 
        - is true. For other values of ch, it is the - smallest value k such that: -
        - (this.codePointAt(k) == ch)  && (k >= fromIndex)
        - 
        - is true. In either case, if no such character occurs in this - string at or after position fromIndex, then - -1 is returned. - -

        - There is no restriction on the value of fromIndex. If it - is negative, it has the same effect as if it were zero: this entire - string may be searched. If it is greater than the length of this - string, it has the same effect as if it were equal to the length of - this string: -1 is returned. - -

        All indices are specified in char values - (Unicode code units).

        -
        -
        Parameters:
        -
        ch - a character (Unicode code point).
        -
        fromIndex - the index to start the search from.
        -
        Returns:
        -
        the index of the first occurrence of the character in the - character sequence represented by this object that is greater - than or equal to fromIndex, or -1 - if the character does not occur.
        -
        -
      • -
      - - - -
        -
      • -

        lastIndexOf

        -
        public int lastIndexOf(int ch)
        -
        Returns the index within this string of the last occurrence of - the specified character. For values of ch in the - range from 0 to 0xFFFF (inclusive), the index (in Unicode code - units) returned is the largest value k such that: -
        - this.charAt(k) == ch
        - 
        - is true. For other values of ch, it is the - largest value k such that: -
        - this.codePointAt(k) == ch
        - 
        - is true. In either case, if no such character occurs in this - string, then -1 is returned. The - String is searched backwards starting at the last - character.
        -
        -
        Parameters:
        -
        ch - a character (Unicode code point).
        -
        Returns:
        -
        the index of the last occurrence of the character in the - character sequence represented by this object, or - -1 if the character does not occur.
        -
        -
      • -
      - - - -
        -
      • -

        lastIndexOf

        -
        public int lastIndexOf(int ch,
        -                       int fromIndex)
        -
        Returns the index within this string of the last occurrence of - the specified character, searching backward starting at the - specified index. For values of ch in the range - from 0 to 0xFFFF (inclusive), the index returned is the largest - value k such that: -
        - (this.charAt(k) == ch)  && (k <= fromIndex)
        - 
        - is true. For other values of ch, it is the - largest value k such that: -
        - (this.codePointAt(k) == ch)  && (k <= fromIndex)
        - 
        - is true. In either case, if no such character occurs in this - string at or before position fromIndex, then - -1 is returned. - -

        All indices are specified in char values - (Unicode code units).

        -
        -
        Parameters:
        -
        ch - a character (Unicode code point).
        -
        fromIndex - the index to start the search from. There is no - restriction on the value of fromIndex. If it is - greater than or equal to the length of this string, it has - the same effect as if it were equal to one less than the - length of this string: this entire string may be searched. - If it is negative, it has the same effect as if it were -1: - -1 is returned.
        -
        Returns:
        -
        the index of the last occurrence of the character in the - character sequence represented by this object that is less - than or equal to fromIndex, or -1 - if the character does not occur before that point.
        -
        -
      • -
      - - - -
        -
      • -

        indexOf

        -
        public int indexOf(String str)
        -
        Returns the index within this string of the first occurrence of the - specified substring. - -

        The returned index is the smallest value k for which: -

        - this.startsWith(str, k)
        - 
        - If no such value of k exists, then -1 is returned.
        -
        -
        Parameters:
        -
        str - the substring to search for.
        -
        Returns:
        -
        the index of the first occurrence of the specified substring, - or -1 if there is no such occurrence.
        -
        -
      • -
      - - - -
        -
      • -

        indexOf

        -
        public int indexOf(String str,
        -                   int fromIndex)
        -
        Returns the index within this string of the first occurrence of the - specified substring, starting at the specified index. - -

        The returned index is the smallest value k for which: -

        - k >= fromIndex  && this.startsWith(str, k)
        - 
        - If no such value of k exists, then -1 is returned.
        -
        -
        Parameters:
        -
        str - the substring to search for.
        -
        fromIndex - the index from which to start the search.
        -
        Returns:
        -
        the index of the first occurrence of the specified substring, - starting at the specified index, - or -1 if there is no such occurrence.
        -
        -
      • -
      - - - -
        -
      • -

        lastIndexOf

        -
        public int lastIndexOf(String str)
        -
        Returns the index within this string of the last occurrence of the - specified substring. The last occurrence of the empty string "" - is considered to occur at the index value this.length(). - -

        The returned index is the largest value k for which: -

        - this.startsWith(str, k)
        - 
        - If no such value of k exists, then -1 is returned.
        -
        -
        Parameters:
        -
        str - the substring to search for.
        -
        Returns:
        -
        the index of the last occurrence of the specified substring, - or -1 if there is no such occurrence.
        -
        -
      • -
      - - - -
        -
      • -

        lastIndexOf

        -
        public int lastIndexOf(String str,
        -                       int fromIndex)
        -
        Returns the index within this string of the last occurrence of the - specified substring, searching backward starting at the specified index. - -

        The returned index is the largest value k for which: -

        - k  <= fromIndex  && this.startsWith(str, k)
        - 
        - If no such value of k exists, then -1 is returned.
        -
        -
        Parameters:
        -
        str - the substring to search for.
        -
        fromIndex - the index to start the search from.
        -
        Returns:
        -
        the index of the last occurrence of the specified substring, - searching backward from the specified index, - or -1 if there is no such occurrence.
        -
        -
      • -
      - - - -
        -
      • -

        substring

        -
        public String substring(int beginIndex)
        -
        Returns a string that is a substring of this string. The - substring begins with the character at the specified index and - extends to the end of this string.

        - Examples: -

        - "unhappy".substring(2) returns "happy"
        - "Harbison".substring(3) returns "bison"
        - "emptiness".substring(9) returns "" (an empty string)
        - 
        -
        -
        Parameters:
        -
        beginIndex - the beginning index, inclusive.
        -
        Returns:
        -
        the specified substring.
        -
        Throws:
        -
        IndexOutOfBoundsException - if - beginIndex is negative or larger than the - length of this String object.
        -
        -
      • -
      - - - -
        -
      • -

        substring

        -
        public String substring(int beginIndex,
        -                        int endIndex)
        -
        Returns a string that is a substring of this string. The - substring begins at the specified beginIndex and - extends to the character at index endIndex - 1. - Thus the length of the substring is endIndex-beginIndex. -

        - Examples: -

        - "hamburger".substring(4, 8) returns "urge"
        - "smiles".substring(1, 5) returns "mile"
        - 
        -
        -
        Parameters:
        -
        beginIndex - the beginning index, inclusive.
        -
        endIndex - the ending index, exclusive.
        -
        Returns:
        -
        the specified substring.
        -
        Throws:
        -
        IndexOutOfBoundsException - if the - beginIndex is negative, or - endIndex is larger than the length of - this String object, or - beginIndex is larger than - endIndex.
        -
        -
      • -
      - - - -
        -
      • -

        subSequence

        -
        public CharSequence subSequence(int beginIndex,
        -                                int endIndex)
        -
        Returns a character sequence that is a subsequence of this sequence. - -

        An invocation of this method of the form - -

        - str.subSequence(begin, end)
        - - behaves in exactly the same way as the invocation - -
        - str.substring(begin, end)
        -
        -
        Specified by:
        -
        subSequence in interface CharSequence
        -
        API Note:
        -
        This method is defined so that the String class can implement - the CharSequence interface.
        -
        Parameters:
        -
        beginIndex - the begin index, inclusive.
        -
        endIndex - the end index, exclusive.
        -
        Returns:
        -
        the specified subsequence.
        -
        Throws:
        -
        IndexOutOfBoundsException - if beginIndex or endIndex is negative, - if endIndex is greater than length(), - or if beginIndex is greater than endIndex
        -
        Since:
        -
        1.4
        -
        -
      • -
      - - - -
        -
      • -

        concat

        -
        public String concat(String str)
        -
        Concatenates the specified string to the end of this string. -

        - If the length of the argument string is 0, then this - String object is returned. Otherwise, a - String object is returned that represents a character - sequence that is the concatenation of the character sequence - represented by this String object and the character - sequence represented by the argument string.

        - Examples: -

        - "cares".concat("s") returns "caress"
        - "to".concat("get").concat("her") returns "together"
        - 
        -
        -
        Parameters:
        -
        str - the String that is concatenated to the end - of this String.
        -
        Returns:
        -
        a string that represents the concatenation of this object's - characters followed by the string argument's characters.
        -
        -
      • -
      - - - -
        -
      • -

        replace

        -
        public String replace(char oldChar,
        -                      char newChar)
        -
        Returns a string resulting from replacing all occurrences of - oldChar in this string with newChar. -

        - If the character oldChar does not occur in the - character sequence represented by this String object, - then a reference to this String object is returned. - Otherwise, a String object is returned that - represents a character sequence identical to the character sequence - represented by this String object, except that every - occurrence of oldChar is replaced by an occurrence - of newChar. -

        - Examples: -

        - "mesquite in your cellar".replace('e', 'o')
        -         returns "mosquito in your collar"
        - "the war of baronets".replace('r', 'y')
        -         returns "the way of bayonets"
        - "sparring with a purple porpoise".replace('p', 't')
        -         returns "starring with a turtle tortoise"
        - "JonL".replace('q', 'x') returns "JonL" (no change)
        - 
        -
        -
        Parameters:
        -
        oldChar - the old character.
        -
        newChar - the new character.
        -
        Returns:
        -
        a string derived from this string by replacing every - occurrence of oldChar with newChar.
        -
        -
      • -
      - - - -
        -
      • -

        matches

        -
        public boolean matches(String regex)
        -
        Tells whether or not this string matches the given regular expression. - -

        An invocation of this method of the form - str.matches(regex) yields exactly the - same result as the expression - -

        - Pattern.matches(regex, str) -
        -
        -
        Parameters:
        -
        regex - the regular expression to which this string is to be matched
        -
        Returns:
        -
        true if, and only if, this string matches the - given regular expression
        -
        Throws:
        -
        PatternSyntaxException - if the regular expression's syntax is invalid
        -
        Since:
        -
        1.4
        -
        See Also:
        -
        Pattern
        -
        -
      • -
      - - - -
        -
      • -

        contains

        -
        public boolean contains(CharSequence s)
        -
        Returns true if and only if this string contains the specified - sequence of char values.
        -
        -
        Parameters:
        -
        s - the sequence to search for
        -
        Returns:
        -
        true if this string contains s, false otherwise
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        replaceFirst

        -
        public String replaceFirst(String regex,
        -                           String replacement)
        -
        Replaces the first substring of this string that matches the given regular expression with the - given replacement. - -

        An invocation of this method of the form - str.replaceFirst(regex, repl) - yields exactly the same result as the expression - -

        - - Pattern.compile(regex).matcher(str).replaceFirst(repl) - -
        - -

        - Note that backslashes (\) and dollar signs ($) in the - replacement string may cause the results to be different than if it were - being treated as a literal replacement string; see - Matcher.replaceFirst(java.lang.String). - Use Matcher.quoteReplacement(java.lang.String) to suppress the special - meaning of these characters, if desired.

        -
        -
        Parameters:
        -
        regex - the regular expression to which this string is to be matched
        -
        replacement - the string to be substituted for the first match
        -
        Returns:
        -
        The resulting String
        -
        Throws:
        -
        PatternSyntaxException - if the regular expression's syntax is invalid
        -
        Since:
        -
        1.4
        -
        See Also:
        -
        Pattern
        -
        -
      • -
      - - - -
        -
      • -

        replaceAll

        -
        public String replaceAll(String regex,
        -                         String replacement)
        -
        Replaces each substring of this string that matches the given regular expression with the - given replacement. - -

        An invocation of this method of the form - str.replaceAll(regex, repl) - yields exactly the same result as the expression - -

        - - Pattern.compile(regex).matcher(str).replaceAll(repl) - -
        - -

        - Note that backslashes (\) and dollar signs ($) in the - replacement string may cause the results to be different than if it were - being treated as a literal replacement string; see - Matcher.replaceAll. - Use Matcher.quoteReplacement(java.lang.String) to suppress the special - meaning of these characters, if desired.

        -
        -
        Parameters:
        -
        regex - the regular expression to which this string is to be matched
        -
        replacement - the string to be substituted for each match
        -
        Returns:
        -
        The resulting String
        -
        Throws:
        -
        PatternSyntaxException - if the regular expression's syntax is invalid
        -
        Since:
        -
        1.4
        -
        See Also:
        -
        Pattern
        -
        -
      • -
      - - - -
        -
      • -

        replace

        -
        public String replace(CharSequence target,
        -                      CharSequence replacement)
        -
        Replaces each substring of this string that matches the literal target - sequence with the specified literal replacement sequence. The - replacement proceeds from the beginning of the string to the end, for - example, replacing "aa" with "b" in the string "aaa" will result in - "ba" rather than "ab".
        -
        -
        Parameters:
        -
        target - The sequence of char values to be replaced
        -
        replacement - The replacement sequence of char values
        -
        Returns:
        -
        The resulting string
        -
        Since:
        -
        1.5
        -
        -
      • -
      - - - -
        -
      • -

        split

        -
        public String[] split(String regex,
        -                      int limit)
        -
        Splits this string around matches of the given - regular expression. - -

        The array returned by this method contains each substring of this - string that is terminated by another substring that matches the given - expression or is terminated by the end of the string. The substrings in - the array are in the order in which they occur in this string. If the - expression does not match any part of the input then the resulting array - has just one element, namely this string. - -

        When there is a positive-width match at the beginning of this - string then an empty leading substring is included at the beginning - of the resulting array. A zero-width match at the beginning however - never produces such empty leading substring. - -

        The limit parameter controls the number of times the - pattern is applied and therefore affects the length of the resulting - array. If the limit n is greater than zero then the pattern - will be applied at most n - 1 times, the array's - length will be no greater than n, and the array's last entry - will contain all input beyond the last matched delimiter. If n - is non-positive then the pattern will be applied as many times as - possible and the array can have any length. If n is zero then - the pattern will be applied as many times as possible, the array can - have any length, and trailing empty strings will be discarded. - -

        The string "boo:and:foo", for example, yields the - following results with these parameters: - -

        - - - - - - - - - - - - - - - - - - - - - - - -
        RegexLimitResult
        :2{ "boo", "and:foo" }
        :5{ "boo", "and", "foo" }
        :-2{ "boo", "and", "foo" }
        o5{ "b", "", ":and:f", "", "" }
        o-2{ "b", "", ":and:f", "", "" }
        o0{ "b", "", ":and:f" }
        - -

        An invocation of this method of the form - str.split(regex, n) - yields the same result as the expression - -

        - - Pattern.compile(regex).split(strn) - -
        -
        -
        Parameters:
        -
        regex - the delimiting regular expression
        -
        limit - the result threshold, as described above
        -
        Returns:
        -
        the array of strings computed by splitting this string - around matches of the given regular expression
        -
        Throws:
        -
        PatternSyntaxException - if the regular expression's syntax is invalid
        -
        Since:
        -
        1.4
        -
        See Also:
        -
        Pattern
        -
        -
      • -
      - - - -
        -
      • -

        split

        -
        public String[] split(String regex)
        -
        Splits this string around matches of the given regular expression. - -

        This method works as if by invoking the two-argument split method with the given expression and a limit - argument of zero. Trailing empty strings are therefore not included in - the resulting array. - -

        The string "boo:and:foo", for example, yields the following - results with these expressions: - -

        - - - - - - - - -
        RegexResult
        :{ "boo", "and", "foo" }
        o{ "b", "", ":and:f" }
        -
        -
        Parameters:
        -
        regex - the delimiting regular expression
        -
        Returns:
        -
        the array of strings computed by splitting this string - around matches of the given regular expression
        -
        Throws:
        -
        PatternSyntaxException - if the regular expression's syntax is invalid
        -
        Since:
        -
        1.4
        -
        See Also:
        -
        Pattern
        -
        -
      • -
      - - - -
        -
      • -

        join

        -
        public static String join(CharSequence delimiter,
        -                          CharSequence... elements)
        -
        Returns a new String composed of copies of the - CharSequence elements joined together with a copy of - the specified delimiter. - -
        For example, -
        
        -     String message = String.join("-", "Java", "is", "cool");
        -     // message returned is: "Java-is-cool"
        - 
        - - Note that if an element is null, then "null" is added.
        -
        -
        Parameters:
        -
        delimiter - the delimiter that separates each element
        -
        elements - the elements to join together.
        -
        Returns:
        -
        a new String that is composed of the elements - separated by the delimiter
        -
        Throws:
        -
        NullPointerException - If delimiter or elements - is null
        -
        Since:
        -
        1.8
        -
        See Also:
        -
        StringJoiner
        -
        -
      • -
      - - - -
        -
      • -

        join

        -
        public static String join(CharSequence delimiter,
        -                          Iterable<? extends CharSequence> elements)
        -
        Returns a new String composed of copies of the - CharSequence elements joined together with a copy of the - specified delimiter. - -
        For example, -
        
        -     List<String> strings = new LinkedList<>();
        -     strings.add("Java");strings.add("is");
        -     strings.add("cool");
        -     String message = String.join(" ", strings);
        -     //message returned is: "Java is cool"
        -
        -     Set<String> strings = new LinkedHashSet<>();
        -     strings.add("Java"); strings.add("is");
        -     strings.add("very"); strings.add("cool");
        -     String message = String.join("-", strings);
        -     //message returned is: "Java-is-very-cool"
        - 
        - - Note that if an individual element is null, then "null" is added.
        -
        -
        Parameters:
        -
        delimiter - a sequence of characters that is used to separate each - of the elements in the resulting String
        -
        elements - an Iterable that will have its elements - joined together.
        -
        Returns:
        -
        a new String that is composed from the elements - argument
        -
        Throws:
        -
        NullPointerException - If delimiter or elements - is null
        -
        Since:
        -
        1.8
        -
        See Also:
        -
        join(CharSequence,CharSequence...), -StringJoiner
        -
        -
      • -
      - - - -
        -
      • -

        toLowerCase

        -
        public String toLowerCase(Locale locale)
        -
        Converts all of the characters in this String to lower - case using the rules of the given Locale. Case mapping is based - on the Unicode Standard version specified by the Character - class. Since case mappings are not always 1:1 char mappings, the resulting - String may be a different length than the original String. -

        - Examples of lowercase mappings are in the following table: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Language Code of LocaleUpper CaseLower CaseDescription
        tr (Turkish)\u0130\u0069capital letter I with dot above -> small letter i
        tr (Turkish)\u0049\u0131capital letter I -> small letter dotless i
        (all)French Friesfrench frieslowercased all chars in String
        (all)capiotacapchi - capthetacapupsil - capsigmaiotachi - thetaupsilon - sigmalowercased all chars in String

        -
        -
        Parameters:
        -
        locale - use the case transformation rules for this locale
        -
        Returns:
        -
        the String, converted to lowercase.
        -
        Since:
        -
        1.1
        -
        See Also:
        -
        toLowerCase(), -toUpperCase(), -toUpperCase(Locale)
        -
        -
      • -
      - - - -
        -
      • -

        toLowerCase

        -
        public String toLowerCase()
        -
        Converts all of the characters in this String to lower - case using the rules of the default locale. This is equivalent to calling - toLowerCase(Locale.getDefault()). -

        - Note: This method is locale sensitive, and may produce unexpected - results if used for strings that are intended to be interpreted locale - independently. - Examples are programming language identifiers, protocol keys, and HTML - tags. - For instance, "TITLE".toLowerCase() in a Turkish locale - returns "t\u0131tle", where '\u0131' is the - LATIN SMALL LETTER DOTLESS I character. - To obtain correct results for locale insensitive strings, use - toLowerCase(Locale.ROOT). -

        -
        -
        Returns:
        -
        the String, converted to lowercase.
        -
        See Also:
        -
        toLowerCase(Locale)
        -
        -
      • -
      - - - -
        -
      • -

        toUpperCase

        -
        public String toUpperCase(Locale locale)
        -
        Converts all of the characters in this String to upper - case using the rules of the given Locale. Case mapping is based - on the Unicode Standard version specified by the Character - class. Since case mappings are not always 1:1 char mappings, the resulting - String may be a different length than the original String. -

        - Examples of locale-sensitive and 1:M case mappings are in the following table. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Language Code of LocaleLower CaseUpper CaseDescription
        tr (Turkish)\u0069\u0130small letter i -> capital letter I with dot above
        tr (Turkish)\u0131\u0049small letter dotless i -> capital letter I
        (all)\u00df\u0053 \u0053small letter sharp s -> two letters: SS
        (all)FahrvergnügenFAHRVERGNÜGEN

        -
        -
        Parameters:
        -
        locale - use the case transformation rules for this locale
        -
        Returns:
        -
        the String, converted to uppercase.
        -
        Since:
        -
        1.1
        -
        See Also:
        -
        toUpperCase(), -toLowerCase(), -toLowerCase(Locale)
        -
        -
      • -
      - - - -
        -
      • -

        toUpperCase

        -
        public String toUpperCase()
        -
        Converts all of the characters in this String to upper - case using the rules of the default locale. This method is equivalent to - toUpperCase(Locale.getDefault()). -

        - Note: This method is locale sensitive, and may produce unexpected - results if used for strings that are intended to be interpreted locale - independently. - Examples are programming language identifiers, protocol keys, and HTML - tags. - For instance, "title".toUpperCase() in a Turkish locale - returns "T\u0130TLE", where '\u0130' is the - LATIN CAPITAL LETTER I WITH DOT ABOVE character. - To obtain correct results for locale insensitive strings, use - toUpperCase(Locale.ROOT). -

        -
        -
        Returns:
        -
        the String, converted to uppercase.
        -
        See Also:
        -
        toUpperCase(Locale)
        -
        -
      • -
      - - - -
        -
      • -

        trim

        -
        public String trim()
        -
        Returns a string whose value is this string, with any leading and trailing - whitespace removed. -

        - If this String object represents an empty character - sequence, or the first and last characters of character sequence - represented by this String object both have codes - greater than '\u0020' (the space character), then a - reference to this String object is returned. -

        - Otherwise, if there is no character with a code greater than - '\u0020' in the string, then a - String object representing an empty string is - returned. -

        - Otherwise, let k be the index of the first character in the - string whose code is greater than '\u0020', and let - m be the index of the last character in the string whose code - is greater than '\u0020'. A String - object is returned, representing the substring of this string that - begins with the character at index k and ends with the - character at index m-that is, the result of - this.substring(k, m + 1). -

        - This method may be used to trim whitespace (as defined above) from - the beginning and end of a string.

        -
        -
        Returns:
        -
        A string whose value is this string, with any leading and trailing white - space removed, or this string if it has no leading or - trailing white space.
        -
        -
      • -
      - - - -
        -
      • -

        toString

        -
        public String toString()
        -
        This object (which is already a string!) is itself returned.
        -
        -
        Specified by:
        -
        toString in interface CharSequence
        -
        Overrides:
        -
        toString in class Object
        -
        Returns:
        -
        the string itself.
        -
        -
      • -
      - - - -
        -
      • -

        toCharArray

        -
        public char[] toCharArray()
        -
        Converts this string to a new character array.
        -
        -
        Returns:
        -
        a newly allocated character array whose length is the length - of this string and whose contents are initialized to contain - the character sequence represented by this string.
        -
        -
      • -
      - - - -
        -
      • -

        format

        -
        public static String format(String format,
        -                            Object... args)
        -
        Returns a formatted string using the specified format string and - arguments. - -

        The locale always used is the one returned by Locale.getDefault().

        -
        -
        Parameters:
        -
        format - A format string
        -
        args - Arguments referenced by the format specifiers in the format - string. If there are more arguments than format specifiers, the - extra arguments are ignored. The number of arguments is - variable and may be zero. The maximum number of arguments is - limited by the maximum dimension of a Java array as defined by - The Java™ Virtual Machine Specification. - The behaviour on a - null argument depends on the conversion.
        -
        Returns:
        -
        A formatted string
        -
        Throws:
        -
        IllegalFormatException - If a format string contains an illegal syntax, a format - specifier that is incompatible with the given arguments, - insufficient arguments given the format string, or other - illegal conditions. For specification of all possible - formatting errors, see the Details section of the - formatter class specification.
        -
        Since:
        -
        1.5
        -
        See Also:
        -
        Formatter
        -
        -
      • -
      - - - -
        -
      • -

        format

        -
        public static String format(Locale l,
        -                            String format,
        -                            Object... args)
        -
        Returns a formatted string using the specified locale, format string, - and arguments.
        -
        -
        Parameters:
        -
        l - The locale to apply during - formatting. If l is null then no localization - is applied.
        -
        format - A format string
        -
        args - Arguments referenced by the format specifiers in the format - string. If there are more arguments than format specifiers, the - extra arguments are ignored. The number of arguments is - variable and may be zero. The maximum number of arguments is - limited by the maximum dimension of a Java array as defined by - The Java™ Virtual Machine Specification. - The behaviour on a - null argument depends on the - conversion.
        -
        Returns:
        -
        A formatted string
        -
        Throws:
        -
        IllegalFormatException - If a format string contains an illegal syntax, a format - specifier that is incompatible with the given arguments, - insufficient arguments given the format string, or other - illegal conditions. For specification of all possible - formatting errors, see the Details section of the - formatter class specification
        -
        Since:
        -
        1.5
        -
        See Also:
        -
        Formatter
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(Object obj)
        -
        Returns the string representation of the Object argument.
        -
        -
        Parameters:
        -
        obj - an Object.
        -
        Returns:
        -
        if the argument is null, then a string equal to - "null"; otherwise, the value of - obj.toString() is returned.
        -
        See Also:
        -
        Object.toString()
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(char[] data)
        -
        Returns the string representation of the char array - argument. The contents of the character array are copied; subsequent - modification of the character array does not affect the returned - string.
        -
        -
        Parameters:
        -
        data - the character array.
        -
        Returns:
        -
        a String that contains the characters of the - character array.
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(char[] data,
        -                             int offset,
        -                             int count)
        -
        Returns the string representation of a specific subarray of the - char array argument. -

        - The offset argument is the index of the first - character of the subarray. The count argument - specifies the length of the subarray. The contents of the subarray - are copied; subsequent modification of the character array does not - affect the returned string.

        -
        -
        Parameters:
        -
        data - the character array.
        -
        offset - initial offset of the subarray.
        -
        count - length of the subarray.
        -
        Returns:
        -
        a String that contains the characters of the - specified subarray of the character array.
        -
        Throws:
        -
        IndexOutOfBoundsException - if offset is - negative, or count is negative, or - offset+count is larger than - data.length.
        -
        -
      • -
      - - - -
        -
      • -

        copyValueOf

        -
        public static String copyValueOf(char[] data,
        -                                 int offset,
        -                                 int count)
        - -
        -
        Parameters:
        -
        data - the character array.
        -
        offset - initial offset of the subarray.
        -
        count - length of the subarray.
        -
        Returns:
        -
        a String that contains the characters of the - specified subarray of the character array.
        -
        Throws:
        -
        IndexOutOfBoundsException - if offset is - negative, or count is negative, or - offset+count is larger than - data.length.
        -
        -
      • -
      - - - -
        -
      • -

        copyValueOf

        -
        public static String copyValueOf(char[] data)
        -
        Equivalent to valueOf(char[]).
        -
        -
        Parameters:
        -
        data - the character array.
        -
        Returns:
        -
        a String that contains the characters of the - character array.
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(boolean b)
        -
        Returns the string representation of the boolean argument.
        -
        -
        Parameters:
        -
        b - a boolean.
        -
        Returns:
        -
        if the argument is true, a string equal to - "true" is returned; otherwise, a string equal to - "false" is returned.
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(char c)
        -
        Returns the string representation of the char - argument.
        -
        -
        Parameters:
        -
        c - a char.
        -
        Returns:
        -
        a string of length 1 containing - as its single character the argument c.
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(int i)
        -
        Returns the string representation of the int argument. -

        - The representation is exactly the one returned by the - Integer.toString method of one argument.

        -
        -
        Parameters:
        -
        i - an int.
        -
        Returns:
        -
        a string representation of the int argument.
        -
        See Also:
        -
        Integer.toString(int, int)
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(long l)
        -
        Returns the string representation of the long argument. -

        - The representation is exactly the one returned by the - Long.toString method of one argument.

        -
        -
        Parameters:
        -
        l - a long.
        -
        Returns:
        -
        a string representation of the long argument.
        -
        See Also:
        -
        Long.toString(long)
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(float f)
        -
        Returns the string representation of the float argument. -

        - The representation is exactly the one returned by the - Float.toString method of one argument.

        -
        -
        Parameters:
        -
        f - a float.
        -
        Returns:
        -
        a string representation of the float argument.
        -
        See Also:
        -
        Float.toString(float)
        -
        -
      • -
      - - - -
        -
      • -

        valueOf

        -
        public static String valueOf(double d)
        -
        Returns the string representation of the double argument. -

        - The representation is exactly the one returned by the - Double.toString method of one argument.

        -
        -
        Parameters:
        -
        d - a double.
        -
        Returns:
        -
        a string representation of the double argument.
        -
        See Also:
        -
        Double.toString(double)
        -
        -
      • -
      - - - -
        -
      • -

        intern

        -
        public String intern()
        -
        Returns a canonical representation for the string object. -

        - A pool of strings, initially empty, is maintained privately by the - class String. -

        - When the intern method is invoked, if the pool already contains a - string equal to this String object as determined by - the equals(Object) method, then the string from the pool is - returned. Otherwise, this String object is added to the - pool and a reference to this String object is returned. -

        - It follows that for any two strings s and t, - s.intern() == t.intern() is true - if and only if s.equals(t) is true. -

        - All literal strings and string-valued constant expressions are - interned. String literals are defined in section 3.10.5 of the - The Java™ Language Specification.

        -
        -
        Returns:
        -
        a string that has the same contents as this string, but is - guaranteed to be from a pool of unique strings.
        -
        -
      • -
      -
    • -
    -
  • -
-
-
- - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - -

Submit a bug or feature
For further API reference and developer documentation, see Java SE Documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.
Copyright © 1993, 2014, Oracle and/or its affiliates. All rights reserved.

- - diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/StringBufferInputStream.html b/src/test/resources/com/gmail/inverseconduit/javadoc/StringBufferInputStream.html deleted file mode 100644 index 626ffa3..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/StringBufferInputStream.html +++ /dev/null @@ -1,557 +0,0 @@ - - - - - -StringBufferInputStream (Java Platform SE 8 ) - - - - - - - - - - - - - - - - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - - -
-
compact1, compact2, compact3
-
java.io
-

Class StringBufferInputStream

-
-
- -
-
    -
  • -
    -
    All Implemented Interfaces:
    -
    Closeable, AutoCloseable
    -
    -
    -
    Deprecated.  -
    This class does not properly convert characters into bytes. As - of JDK 1.1, the preferred way to create a stream from a - string is via the StringReader class.
    -
    -
    -
    @Deprecated
    -public class StringBufferInputStream
    -extends InputStream
    -
    This class allows an application to create an input stream in - which the bytes read are supplied by the contents of a string. - Applications can also read bytes from a byte array by using a - ByteArrayInputStream. -

    - Only the low eight bits of each character in the string are used by - this class.

    -
    -
    Since:
    -
    JDK1.0
    -
    See Also:
    -
    ByteArrayInputStream, -StringReader
    -
    -
  • -
-
-
-
    -
  • - -
      -
    • - - -

      Field Summary

      - - - - - - - - - - - - - - - - - - -
      Fields 
      Modifier and TypeField and Description
      protected Stringbuffer -
      Deprecated. 
      -
      The string from which bytes are read.
      -
      protected intcount -
      Deprecated. 
      -
      The number of valid characters in the input stream buffer.
      -
      protected intpos -
      Deprecated. 
      -
      The index of the next character to read from the input stream buffer.
      -
      -
    • -
    - -
      -
    • - - -

      Constructor Summary

      - - - - - - - - -
      Constructors 
      Constructor and Description
      StringBufferInputStream(String s) -
      Deprecated. 
      -
      Creates a string input stream to read data from the specified string.
      -
      -
    • -
    - - -
  • -
-
-
-
    -
  • - -
      -
    • - - -

      Field Detail

      - - - -
        -
      • -

        buffer

        -
        protected String buffer
        -
        Deprecated. 
        -
        The string from which bytes are read.
        -
      • -
      - - - -
        -
      • -

        pos

        -
        protected int pos
        -
        Deprecated. 
        -
        The index of the next character to read from the input stream buffer.
        -
        -
        See Also:
        -
        buffer
        -
        -
      • -
      - - - -
        -
      • -

        count

        -
        protected int count
        -
        Deprecated. 
        -
        The number of valid characters in the input stream buffer.
        -
        -
        See Also:
        -
        buffer
        -
        -
      • -
      -
    • -
    - -
      -
    • - - -

      Constructor Detail

      - - - -
        -
      • -

        StringBufferInputStream

        -
        public StringBufferInputStream(String s)
        -
        Deprecated. 
        -
        Creates a string input stream to read data from the specified string.
        -
        -
        Parameters:
        -
        s - the underlying input buffer.
        -
        -
      • -
      -
    • -
    - -
      -
    • - - -

      Method Detail

      - - - -
        -
      • -

        read

        -
        public int read()
        -
        Deprecated. 
        -
        Reads the next byte of data from this input stream. The value - byte is returned as an int in the range - 0 to 255. If no byte is available - because the end of the stream has been reached, the value - -1 is returned. -

        - The read method of - StringBufferInputStream cannot block. It returns the - low eight bits of the next character in this input stream's buffer.

        -
        -
        Specified by:
        -
        read in class InputStream
        -
        Returns:
        -
        the next byte of data, or -1 if the end of the - stream is reached.
        -
        -
      • -
      - - - -
        -
      • -

        read

        -
        public int read(byte[] b,
        -                int off,
        -                int len)
        -
        Deprecated. 
        -
        Reads up to len bytes of data from this input stream - into an array of bytes. -

        - The read method of - StringBufferInputStream cannot block. It copies the - low eight bits from the characters in this input stream's buffer into - the byte array argument.

        -
        -
        Overrides:
        -
        read in class InputStream
        -
        Parameters:
        -
        b - the buffer into which the data is read.
        -
        off - the start offset of the data.
        -
        len - the maximum number of bytes read.
        -
        Returns:
        -
        the total number of bytes read into the buffer, or - -1 if there is no more data because the end of - the stream has been reached.
        -
        See Also:
        -
        InputStream.read()
        -
        -
      • -
      - - - -
        -
      • -

        skip

        -
        public long skip(long n)
        -
        Deprecated. 
        -
        Skips n bytes of input from this input stream. Fewer - bytes might be skipped if the end of the input stream is reached.
        -
        -
        Overrides:
        -
        skip in class InputStream
        -
        Parameters:
        -
        n - the number of bytes to be skipped.
        -
        Returns:
        -
        the actual number of bytes skipped.
        -
        -
      • -
      - - - -
        -
      • -

        available

        -
        public int available()
        -
        Deprecated. 
        -
        Returns the number of bytes that can be read from the input - stream without blocking.
        -
        -
        Overrides:
        -
        available in class InputStream
        -
        Returns:
        -
        the value of count - pos, which is the - number of bytes remaining to be read from the input buffer.
        -
        -
      • -
      - - - -
        -
      • -

        reset

        -
        public void reset()
        -
        Deprecated. 
        -
        Resets the input stream to begin reading from the first character - of this input stream's underlying buffer.
        -
        -
        Overrides:
        -
        reset in class InputStream
        -
        See Also:
        -
        InputStream.mark(int), -IOException
        -
        -
      • -
      -
    • -
    -
  • -
-
-
- - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - -

Submit a bug or feature
For further API reference and developer documentation, see Java SE Documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.
Copyright © 1993, 2014, Oracle and/or its affiliates. All rights reserved.

- - diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/SuppressWarnings.html b/src/test/resources/com/gmail/inverseconduit/javadoc/SuppressWarnings.html deleted file mode 100644 index f301709..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/SuppressWarnings.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - -SuppressWarnings (Java Platform SE 8 ) - - - - - - - - - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - - -
-
java.lang
-

Annotation Type SuppressWarnings

-
-
-
-
    -
  • -
    -
    -
    @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
    - @Retention(value=SOURCE)
    -public @interface SuppressWarnings
    -
    Indicates that the named compiler warnings should be suppressed in the - annotated element (and in all program elements contained in the annotated - element). Note that the set of warnings suppressed in a given element is - a superset of the warnings suppressed in all containing elements. For - example, if you annotate a class to suppress one warning and annotate a - method to suppress another, both warnings will be suppressed in the method. - -

    As a matter of style, programmers should always use this annotation - on the most deeply nested element where it is effective. If you want to - suppress a warning in a particular method, you should annotate that - method rather than its class.

    -
    -
    Since:
    -
    1.5
    -
    See The Java™ Language Specification:
    -
    4.8 Raw Types, 4.12.2 Variables of Reference Type, 5.1.9 Unchecked Conversion, 5.5.2 Checked Casts and Unchecked Casts, 9.6.3.5 @SuppressWarnings
    -
    -
  • -
-
-
-
    -
  • - -
      -
    • - - -

      Required Element Summary

      - - - - - - - - - - -
      Required Elements 
      Modifier and TypeRequired Element and Description
      String[]value -
      The set of warnings that are to be suppressed by the compiler in the - annotated element.
      -
      -
    • -
    -
  • -
-
-
-
    -
  • - -
      -
    • - - -

      Element Detail

      - - - -
        -
      • -

        value

        -
        public abstract String[] value
        -
        The set of warnings that are to be suppressed by the compiler in the - annotated element. Duplicate names are permitted. The second and - successive occurrences of a name are ignored. The presence of - unrecognized warning names is not an error: Compilers must - ignore any warning names they do not recognize. They are, however, - free to emit a warning if an annotation contains an unrecognized - warning name. - -

        The string "unchecked" is used to suppress - unchecked warnings. Compiler vendors should document the - additional warning names they support in conjunction with this - annotation type. They are encouraged to cooperate to ensure - that the same names work across multiple compilers.

        -
        -
        Returns:
        -
        the set of warnings to be suppressed
        -
        -
      • -
      -
    • -
    -
  • -
-
-
- - -
- - - - - - - -
Java™ Platform
Standard Ed. 8
-
- - -

Submit a bug or feature
For further API reference and developer documentation, see Java SE Documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.
Copyright © 1993, 2014, Oracle and/or its affiliates. All rights reserved.

- - diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/java8-allclasses-frame.html b/src/test/resources/com/gmail/inverseconduit/javadoc/java8-allclasses-frame.html deleted file mode 100644 index 99069d1..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/java8-allclasses-frame.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - -All Classes (Java Platform SE 8 ) - - - - - -

All Classes

- - - diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-file.zip b/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-file.zip deleted file mode 100644 index 1d16ba5..0000000 Binary files a/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-file.zip and /dev/null differ diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-without-allclasses-frame.zip b/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-without-allclasses-frame.zip deleted file mode 100644 index 3c31760..0000000 Binary files a/src/test/resources/com/gmail/inverseconduit/javadoc/javadoc-without-allclasses-frame.zip and /dev/null differ diff --git a/src/test/resources/com/gmail/inverseconduit/javadoc/jsoup-allclasses-frame.html b/src/test/resources/com/gmail/inverseconduit/javadoc/jsoup-allclasses-frame.html deleted file mode 100644 index 1a1e49a..0000000 --- a/src/test/resources/com/gmail/inverseconduit/javadoc/jsoup-allclasses-frame.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - -All Classes (jsoup 1.8.1 API) - - - - - - - - - - - -All Classes -
- - - - - -
Attribute -
-Attributes -
-Comment -
-Connection -
-Connection.Base -
- - -