aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/ConfigurationKeys.java2
-rw-r--r--src/core/lombok/core/PostCompiler.java10
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java19
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java2
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java36
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java8
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java55
-rw-r--r--src/delombok/lombok/delombok/PrettyPrinter.java26
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java25
-rw-r--r--src/stubs/com/sun/tools/javac/code/Symbol.java8
-rw-r--r--src/stubs/com/sun/tools/javac/code/Symtab.java2
-rw-r--r--src/support/lombok/website/RunSite.java103
12 files changed, 247 insertions, 49 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 60b98a94..01a4b576 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -140,7 +140,7 @@ public class ConfigurationKeys {
* NB: GWT projects, and probably android projects, should explicitly set this key to {@code true} for the entire project.
*
* <br>
- * <em>BREAKING CHANGE</em>: Starting with lombok v1.16.20, defaults to {@code false} instead of {@code true}, as {@code @ConstructorProperties} requires extra modules in JDK9.
+ * <em>BREAKING CHANGE</em>: Starting with lombok v1.16.20, defaults to {@code true} instead of {@code false}, as {@code @ConstructorProperties} requires extra modules in JDK9.
*
* @see ConfigurationKeys#ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES
* @deprecated Since version 2.0, use {@link #ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES} instead.
diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java
index e17f806e..72f4b3a2 100644
--- a/src/core/lombok/core/PostCompiler.java
+++ b/src/core/lombok/core/PostCompiler.java
@@ -72,10 +72,12 @@ public final class PostCompiler {
// no need to call super
byte[] original = toByteArray();
byte[] copy = null;
- try {
- copy = applyTransformations(original, fileName, diagnostics);
- } catch (Exception e) {
- diagnostics.addWarning(String.format("Error during the transformation of '%s'; no post-compilation has been applied", fileName));
+ if (original.length > 0) {
+ try {
+ copy = applyTransformations(original, fileName, diagnostics);
+ } catch (Exception e) {
+ diagnostics.addWarning(String.format("Error during the transformation of '%s'; no post-compilation has been applied", fileName));
+ }
}
if (copy == null) {
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index c4b60475..92a9a142 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -759,6 +759,7 @@ public class HandlerUtil {
}
private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITH(?:ER)?)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+ private static final Pattern LINE_BREAK_FINDER = Pattern.compile("(\\r?\\n)?");
public static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) {
Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
@@ -767,27 +768,29 @@ public class HandlerUtil {
}
public static String stripSectionsFromJavadoc(String javadoc) {
- Matcher m = SECTION_FINDER.matcher(javadoc);
- if (!m.find()) return javadoc;
+ Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc);
+ if (!sectionMatcher.find()) return javadoc;
- return javadoc.substring(0, m.start());
+ return javadoc.substring(0, sectionMatcher.start());
}
public static String getJavadocSection(String javadoc, String sectionNameSpec) {
String[] sectionNames = sectionNameSpec.split("\\|");
- Matcher m = SECTION_FINDER.matcher(javadoc);
+ Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc);
+ Matcher lineBreakMatcher = LINE_BREAK_FINDER.matcher(javadoc);
int sectionStart = -1;
int sectionEnd = -1;
- while (m.find()) {
+ while (sectionMatcher.find()) {
boolean found = false;
- for (String sectionName : sectionNames) if (m.group(1).equalsIgnoreCase(sectionName)) {
+ for (String sectionName : sectionNames) if (sectionMatcher.group(1).equalsIgnoreCase(sectionName)) {
found = true;
break;
}
if (found) {
- sectionStart = m.end() + 1;
+ lineBreakMatcher.find(sectionMatcher.end());
+ sectionStart = lineBreakMatcher.end();
} else if (sectionStart != -1) {
- sectionEnd = m.start();
+ sectionEnd = sectionMatcher.start();
}
}
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index b45ac72d..d53856af 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -350,7 +350,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
if (!changed) clearChanged();
}
- private static boolean isComplete(CompilationUnitDeclaration unit) {
+ public static boolean isComplete(CompilationUnitDeclaration unit) {
return (unit.bits & ASTNode.HasAllMethodBodies) != 0;
}
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index 6fcde937..59a0709e 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2019 The Project Lombok Authors.
+ * Copyright (C) 2009-2020 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,9 @@ package lombok.eclipse;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
import lombok.ConfigurationKeys;
import lombok.core.LombokConfiguration;
@@ -63,6 +66,7 @@ public class TransformEclipseAST {
public static boolean disableLombok = false;
private static final HistogramTracker lombokTracker;
+ private static Map<CompilationUnitDeclaration, State> transformationStates = Collections.synchronizedMap(new WeakHashMap<CompilationUnitDeclaration, State>());
static {
String v = System.getProperty("lombok.histogram");
@@ -130,6 +134,30 @@ public class TransformEclipseAST {
}
/**
+ * Check if lombok already handled the given AST. This method will return
+ * <code>true</code> once for diet mode and once for full mode.
+ *
+ * The reason for this is that Eclipse invokes the transform method multiple
+ * times during compilation and it is enough to transform it once and not
+ * repeat the whole thing over and over again.
+ *
+ * @param ast The AST node belonging to the compilation unit (java speak for a single source file).
+ * @return <code>true</code> if this AST was already handled by lombok.
+ */
+ public static boolean alreadyTransformed(CompilationUnitDeclaration ast) {
+ State state = transformationStates.get(ast);
+
+ if (state == State.FULL) return true;
+ if (state == State.DIET) {
+ if (!EclipseAST.isComplete(ast)) return true;
+ transformationStates.put(ast, State.FULL);
+ } else {
+ transformationStates.put(ast, State.DIET);
+ }
+ return false;
+ }
+
+ /**
* This method is called immediately after Eclipse finishes building a CompilationUnitDeclaration, which is
* the top-level AST node when Eclipse parses a source file. The signature is 'magic' - you should not
* change it!
@@ -144,6 +172,7 @@ public class TransformEclipseAST {
if (disableLombok) return;
if (Symbols.hasSymbol("lombok.disable")) return;
+ if (alreadyTransformed(ast)) return;
// Do NOT abort if (ast.bits & ASTNode.HasAllMethodBodies) != 0 - that doesn't work.
@@ -243,4 +272,9 @@ public class TransformEclipseAST {
nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority));
}
}
+
+ private static enum State {
+ DIET,
+ FULL
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
index 9343011f..cee3912c 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
@@ -171,10 +171,10 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName
injectMethod(fieldsType, constructor);
}
+ Clinit cli = null;
if (genClinit) {
- Clinit cli = new Clinit(parent.compilationResult);
+ cli = new Clinit(parent.compilationResult);
injectMethod(fieldsType, cli);
- cli.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).scope);
}
for (EclipseNode fieldNode : fields) {
@@ -202,5 +202,9 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName
injectField(fieldsType, constantField);
constantField.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).initializerScope);
}
+
+ if (genClinit) {
+ cli.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).scope);
+ }
}
}
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 8a1ed5e7..2379d0a0 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -39,6 +39,7 @@ import java.util.regex.Pattern;
import javax.lang.model.element.Element;
+import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Scope;
@@ -1126,7 +1127,25 @@ public class JavacHandlerUtil {
}
}
}
-
+
+ static class JCAnnotationReflect {
+ private static Field ATTRIBUTE;
+
+ static {
+ try {
+ ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute");
+ } catch (Exception ignore) {}
+ }
+
+ static Attribute.Compound getAttribute(JCAnnotation jcAnnotation) {
+ try {
+ return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
// jdk9 support, types have changed, names stay the same
static class ClassSymbolMembersField {
private static final Field membersField;
@@ -1177,8 +1196,10 @@ public class JavacHandlerUtil {
* Also takes care of updating the JavacAST.
*/
public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) {
+ Context context = typeNode.getContext();
+ Symtab symtab = Symtab.instance(context);
JCClassDecl type = (JCClassDecl) typeNode.get();
-
+
if (method.getName().contentEquals("<init>")) {
//Scan for default constructor, and remove it.
int idx = 0;
@@ -1195,7 +1216,7 @@ public class JavacHandlerUtil {
idx++;
}
}
-
+
addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
type.defs = type.defs.append(method);
@@ -1203,18 +1224,38 @@ public class JavacHandlerUtil {
List<Symbol.VarSymbol> params = null;
if (method.getParameters() != null && !method.getParameters().isEmpty()) {
ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>();
- for (JCTree.JCVariableDecl param : method.getParameters()) {
- if (param.sym != null) newParams.append(param.sym);
+ for (int i = 0; i < method.getParameters().size(); i++) {
+ JCTree.JCVariableDecl param = method.getParameters().get(i);
+ if (param.sym == null) {
+ Type paramType = paramTypes == null ? param.getType().type : paramTypes.get(i);
+ VarSymbol varSymbol = new VarSymbol(param.mods.flags, param.name, paramType, symtab.noSymbol);
+ List<JCAnnotation> annotations = param.getModifiers().getAnnotations();
+ if (annotations != null && !annotations.isEmpty()) {
+ ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>();
+ for (JCAnnotation jcAnnotation : annotations) {
+ Attribute.Compound attribute = JCAnnotationReflect.getAttribute(jcAnnotation);
+ if (attribute != null) {
+ newAnnotations.append(attribute);
+ }
+ }
+ if (annotations.length() == newAnnotations.length()) {
+ varSymbol.appendAttributes(newAnnotations.toList());
+ }
+ }
+ newParams.append(varSymbol);
+ } else {
+ newParams.append(param.sym);
+ }
}
params = newParams.toList();
if (params.length() != method.getParameters().length()) params = null;
}
fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, params, returnType);
-
+
typeNode.add(method, Kind.METHOD);
}
-
+
private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, List<Symbol.VarSymbol> params, Type returnType) {
if (typeMirror == null || paramTypes == null || returnType == null) return;
ClassSymbol cs = (ClassSymbol) typeMirror;
diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java
index 208b215f..54fa4ebf 100644
--- a/src/delombok/lombok/delombok/PrettyPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyPrinter.java
@@ -188,12 +188,6 @@ public class PrettyPrinter extends JCTree.Visitor {
return getEndPosition(tree, compilationUnit);
}
- private static int lineEndPos(String s, int start) {
- int pos = s.indexOf('\n', start);
- if (pos < 0) pos = s.length();
- return pos;
- }
-
private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned;
public static final class UncheckedIOException extends RuntimeException {
@@ -434,23 +428,25 @@ public class PrettyPrinter extends JCTree.Visitor {
private void printDocComment(JCTree tree) {
String dc = getJavadocFor(tree);
if (dc == null) return;
+
aPrintln("/**");
- int pos = 0;
- int endpos = lineEndPos(dc, pos);
boolean atStart = true;
- while (pos < dc.length()) {
- String line = dc.substring(pos, endpos);
- if (line.trim().isEmpty() && atStart) {
+
+ for (String line : dc.split("\\r?\\n")) {
+ if (atStart && line.trim().isEmpty()) {
atStart = false;
continue;
}
+
atStart = false;
aPrint(" *");
- if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
- println(dc.substring(pos, endpos));
- pos = endpos + 1;
- endpos = lineEndPos(dc, pos);
+ if (!line.isEmpty() && !Character.isWhitespace(line.charAt(0))) {
+ print(" ");
+ }
+
+ println(line);
}
+
aPrintln(" */");
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index 056852c8..f22e78a8 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -35,11 +35,13 @@ import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
@@ -227,14 +229,16 @@ public class PatchVal {
boolean var = isVar(local, scope);
if (!(val || var)) return false;
- StackTraceElement[] st = new Throwable().getStackTrace();
- for (int i = 0; i < st.length - 2 && i < 10; i++) {
- if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) {
- boolean valInForStatement = val &&
- st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") &&
- st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement");
- if (valInForStatement) return false;
- break;
+ if (val) {
+ StackTraceElement[] st = new Throwable().getStackTrace();
+ for (int i = 0; i < st.length - 2 && i < 10; i++) {
+ if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) {
+ boolean valInForStatement =
+ st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") &&
+ st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement");
+ if (valInForStatement) return false;
+ break;
+ }
}
}
@@ -264,6 +268,7 @@ public class PatchVal {
}
TypeBinding resolved = null;
+ Constant oldConstant = init.constant;
try {
resolved = decomponent ? getForEachComponentType(init, scope) : resolveForExpression(init, scope);
} catch (NullPointerException e) {
@@ -280,6 +285,10 @@ public class PatchVal {
} catch (Exception e) {
// Some type thing failed.
}
+ } else {
+ if (init instanceof MessageSend && ((MessageSend) init).actualReceiverType == null) {
+ init.constant = oldConstant;
+ }
}
}
diff --git a/src/stubs/com/sun/tools/javac/code/Symbol.java b/src/stubs/com/sun/tools/javac/code/Symbol.java
index 7324cb8e..15b04148 100644
--- a/src/stubs/com/sun/tools/javac/code/Symbol.java
+++ b/src/stubs/com/sun/tools/javac/code/Symbol.java
@@ -24,7 +24,7 @@ import com.sun.tools.javac.util.Name;
public abstract class Symbol implements Element {
public Type type;
public Name name;
-
+
public long flags() { return 0; }
public boolean isStatic() { return false; }
public boolean isConstructor() { return false; }
@@ -38,7 +38,9 @@ public abstract class Symbol implements Element {
@Override public Name getSimpleName() { return null; }
@Override public java.util.List<Symbol> getEnclosedElements() { return null; }
@Override public Element getEnclosingElement() { return null; }
-
+ public void appendAttributes(List<Attribute.Compound> l) {
+ }
+
public static abstract class TypeSymbol extends Symbol {}
public static class MethodSymbol extends Symbol implements ExecutableElement {
@@ -60,6 +62,8 @@ public abstract class Symbol implements Element {
public static class VarSymbol extends Symbol implements VariableElement {
public Type type;
+ public VarSymbol(long flags, Name name, Type type, Symbol owner) {
+ }
@Override public ElementKind getKind() { return null; }
@Override public Set<Modifier> getModifiers() { return null; }
@Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; }
diff --git a/src/stubs/com/sun/tools/javac/code/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java
index 2b524e4c..8d823531 100644
--- a/src/stubs/com/sun/tools/javac/code/Symtab.java
+++ b/src/stubs/com/sun/tools/javac/code/Symtab.java
@@ -5,6 +5,7 @@ package com.sun.tools.javac.code;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.util.Context;
public class Symtab {
@@ -14,6 +15,7 @@ public class Symtab {
public Type objectType;
public static Symtab instance(Context context) {return null;}
public Type unknownType;
+ public TypeSymbol noSymbol;
// JDK 9
public ModuleSymbol unnamedModule;
diff --git a/src/support/lombok/website/RunSite.java b/src/support/lombok/website/RunSite.java
new file mode 100644
index 00000000..17e158c0
--- /dev/null
+++ b/src/support/lombok/website/RunSite.java
@@ -0,0 +1,103 @@
+package lombok.website;
+
+import static spark.Spark.*;
+
+import java.awt.Desktop;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import spark.Request;
+import spark.Response;
+import spark.Route;
+
+public class RunSite {
+ private static final int DEFAULT_PORT = 4569;
+ private final Path base;
+
+ public RunSite(Path base) {
+ this.base = base;
+ }
+
+ public static void main(String[] args) throws Exception {
+ boolean open = args.length > 1 && args[1].equals("open");
+ new RunSite(Paths.get(args[0])).go(open);
+ }
+
+ private void go(boolean open) throws Exception {
+ port(DEFAULT_PORT);
+ get("/", serve("main.html"));
+ get("/setup/overview", serve("setup/main.html"));
+ get("/setup", serve("setup/main.html"));
+ get("/features", serve("features/index.html"));
+ get("/features/all", serve("features/index.html"));
+ get("/features/experimental/all", serve("features/experimental/index.html"));
+ get("/features/experimental", serve("features/experimental/index.html"));
+
+ serveDir("/", base);
+
+ System.out.println("Serving page from " + base + " -- hit enter to stop");
+ if (open) Opener.open("http://localhost:" + DEFAULT_PORT + "/");
+ System.in.read();
+ System.exit(0);
+ }
+
+ private void serveDir(String sub, Path dir) throws IOException {
+ DirectoryStream<Path> ds = Files.newDirectoryStream(dir);
+ try {
+ for (Path c : ds) {
+ String n = c.getFileName().toString();
+ if (n.equals(".") || n.equals("..")) continue;
+ if (Files.isDirectory(c)) {
+ serveDir(sub + n + "/", c);
+ continue;
+ }
+ String rel = base.relativize(c).toString();
+ get(sub + n, serve(rel));
+ if (n.endsWith(".html")) get(sub + n.substring(0, n.length() - 5), serve(rel));
+ }
+ } finally {
+ ds.close();
+ }
+ }
+
+ private static class Opener {
+ public static void open(String url) throws Exception {
+ Desktop.getDesktop().browse(new URI(url));
+ }
+ }
+
+ private Route serve(final String path) {
+ final Path tgt = base.resolve(path);
+
+ return new Route() {
+ @Override public Object handle(Request req, Response res) throws Exception {
+ res.type(mapMime(path));
+ return Files.readAllBytes(tgt);
+ }
+ };
+ }
+
+ private String mapMime(String path) {
+ if (path.endsWith(".css")) return "text/css; charset=UTF-8";
+ if (path.endsWith(".js")) return "text/javascript; charset=UTF-8";
+ if (path.endsWith(".png")) return "image/png";
+ if (path.endsWith(".gif")) return "image/gif";
+ if (path.endsWith(".jpg")) return "image/jpeg";
+ if (path.endsWith(".mp4")) return "video/mp4";
+ if (path.endsWith(".m4v")) return "video/mp4";
+ if (path.endsWith(".ogv")) return "video/ogg";
+ if (path.endsWith(".webm")) return "video/webm";
+ if (path.endsWith(".ico")) return "image/x-icon";
+ if (path.endsWith(".pdf")) return "application/pdf";
+ if (path.endsWith(".json")) return "application/json";
+ if (path.endsWith(".xml")) return "text/xml";
+ if (path.endsWith(".woff")) return "font/woff";
+ if (path.endsWith(".woff2")) return "font/woff2";
+ if (path.endsWith(".html")) return "text/html; charset=UTF-8";
+ return "text/plain; charset=UTF-8";
+ }
+}