You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by in...@apache.org on 2017/07/28 16:50:44 UTC

[35/50] [abbrv] hadoop git commit: HADOOP-11875. [JDK9] Adding a second copy of Hamlet without _ as a one-character identifier.

http://git-wip-us.apache.org/repos/asf/hadoop/blob/38c6fa5c/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletGen.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletGen.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletGen.java
new file mode 100644
index 0000000..c6ca93c
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletGen.java
@@ -0,0 +1,449 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.hadoop.yarn.webapp.hamlet2;
+
+import com.google.common.collect.Sets;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.yarn.webapp.WebAppException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generates a specific hamlet implementation class from a spec class
+ * using a generic hamlet implementation class.
+ */
+@InterfaceAudience.LimitedPrivate({"YARN", "MapReduce"})
+public class HamletGen {
+  static final Logger LOG = LoggerFactory.getLogger(HamletGen.class);
+  static final Options opts = new Options();
+  static {
+    opts.addOption("h", "help", false, "Print this help message").
+         addOption("s", "spec-class", true,
+                   "The class that holds the spec interfaces. e.g. HamletSpec").
+         addOption("i", "impl-class", true,
+                   "An implementation class. e.g. HamletImpl").
+         addOption("o", "output-class", true, "Output class name").
+         addOption("p", "output-package", true, "Output package name");
+  };
+
+  static final Pattern elementRegex = Pattern.compile("^[A-Z][A-Z0-9]*$");
+
+  int bytes = 0;
+  PrintWriter out;
+  final Set<String> endTagOptional = Sets.newHashSet();
+  final Set<String> inlineElements = Sets.newHashSet();
+  Class<?> top; // html top-level interface
+  String hamlet; // output class simple name;
+  boolean topMode;
+
+  /**
+   * Generate a specific Hamlet implementation from a spec.
+   * @param specClass holds hamlet interfaces. e.g. {@link HamletSpec}
+   * @param implClass a generic hamlet implementation. e.g. {@link HamletImpl}
+   * @param outputName name of the output class. e.g. {@link Hamlet}
+   * @param outputPkg package name of the output class.
+   * @throws IOException
+   */
+  public void generate(Class<?> specClass, Class<?> implClass,
+                       String outputName, String outputPkg) throws IOException {
+    LOG.info("Generating {} using {} and {}", new Object[]{outputName,
+             specClass, implClass});
+    out = new PrintWriter(outputName +".java", "UTF-8");
+    hamlet = basename(outputName);
+    String pkg = pkgName(outputPkg, implClass.getPackage().getName());
+    puts(0, "// Generated by HamletGen. Do NOT edit!\n",
+         "package ", pkg, ";\n",
+         "import java.io.PrintWriter;\n",
+         "import java.util.EnumSet;\n",
+         "import static java.util.EnumSet.*;\n",
+         "import static ", implClass.getName(), ".EOpt.*;\n",
+         "import org.apache.hadoop.yarn.webapp.SubView;");
+    String implClassName = implClass.getSimpleName();
+    if (!implClass.getPackage().getName().equals(pkg)) {
+      puts(0, "import ", implClass.getName(), ';');
+    }
+    puts(0, "\n",
+         "public class ", hamlet, " extends ", implClassName,
+         " implements ", specClass.getSimpleName(), "._Html {\n",
+         "  public ", hamlet, "(PrintWriter out, int nestLevel,",
+         " boolean wasInline) {\n",
+         "    super(out, nestLevel, wasInline);\n",
+         "  }\n\n", // inline is context sensitive
+         "  static EnumSet<EOpt> opt(boolean endTag, boolean inline, ",
+         "boolean pre) {\n",
+         "    EnumSet<EOpt> opts = of(ENDTAG);\n",
+         "    if (!endTag) opts.remove(ENDTAG);\n",
+         "    if (inline) opts.add(INLINE);\n",
+         "    if (pre) opts.add(PRE);\n",
+         "    return opts;\n",
+         "  }");
+    initLut(specClass);
+    genImpl(specClass, implClassName, 1);
+    LOG.info("Generating {} methods", hamlet);
+    genMethods(hamlet, top, 1);
+    puts(0, "}");
+    out.close();
+    LOG.info("Wrote {} bytes to {}.java", bytes, outputName);
+  }
+
+  String basename(String path) {
+    return path.substring(path.lastIndexOf('/') + 1);
+  }
+
+  String pkgName(String pkg, String defaultPkg) {
+    if (pkg == null || pkg.isEmpty()) return defaultPkg;
+    return pkg;
+  }
+
+  void initLut(Class<?> spec) {
+    endTagOptional.clear();
+    inlineElements.clear();
+    for (Class<?> cls : spec.getClasses()) {
+      Annotation a = cls.getAnnotation(HamletSpec.Element.class);
+      if (a != null && !((HamletSpec.Element) a).endTag()) {
+        endTagOptional.add(cls.getSimpleName());
+      }
+      if (cls.getSimpleName().equals("Inline")) {
+        for (Method method : cls.getMethods()) {
+          String retName = method.getReturnType().getSimpleName();
+          if (isElement(retName)) {
+            inlineElements.add(retName);
+          }
+        }
+      }
+    }
+  }
+
+  void genImpl(Class<?> spec, String implClassName, int indent) {
+    String specName = spec.getSimpleName();
+    for (Class<?> cls : spec.getClasses()) {
+      String className = cls.getSimpleName();
+      if (cls.isInterface()) {
+        genFactoryMethods(cls, indent);
+      }
+      if (isElement(className)) {
+        LOG.info("Generating class {}<T>", className);
+        puts(indent, "\n",
+             "public class ", className, "<T extends __>",
+             " extends EImp<T> implements ", specName, ".", className, " {\n",
+             "  public ", className, "(String name, T parent,",
+             " EnumSet<EOpt> opts) {\n",
+             "    super(name, parent, opts);\n",
+             "  }");
+        genMethods(className, cls, indent + 1);
+        puts(indent, "}");
+      } else if (className.equals("_Html")) {
+        top = cls;
+      }
+    }
+  }
+
+  void genFactoryMethods(Class<?> cls, int indent) {
+    for (Method method : cls.getDeclaredMethods()) {
+      String retName = method.getReturnType().getSimpleName();
+      String methodName = method.getName();
+      if (methodName.charAt(0) == '$') continue;
+      if (isElement(retName) && method.getParameterTypes().length == 0) {
+        genFactoryMethod(retName, methodName, indent);
+      }
+    }
+  }
+
+  void genMethods(String className, Class<?> cls, int indent) {
+    topMode = (top != null && cls.equals(top));
+    for (Method method : cls.getMethods()) {
+      String retName = method.getReturnType().getSimpleName();
+      if (method.getName().charAt(0) == '$') {
+        genAttributeMethod(className, method, indent);
+      } else if (isElement(retName)) {
+        genNewElementMethod(className, method, indent);
+      } else {
+        genCurElementMethod(className, method, indent);
+      }
+    }
+  }
+
+  void genAttributeMethod(String className, Method method, int indent) {
+    String methodName = method.getName();
+    String attrName = methodName.substring(1).replace("__", "-");
+    Type[] params = method.getGenericParameterTypes();
+    echo(indent, "\n",
+         "@Override\n",
+         "public ", className, topMode ? " " : "<T> ", methodName, "(");
+    if (params.length == 0) {
+      puts(0, ") {");
+      puts(indent,
+           "  addAttr(\"", attrName, "\", null);\n",
+           "  return this;\n", "}");
+    } else if (params.length == 1) {
+      String typeName = getTypeName(params[0]);
+      puts(0, typeName, " value) {");
+      if (typeName.equals("EnumSet<LinkType>")) {
+        puts(indent,
+             "  addRelAttr(\"", attrName, "\", value);\n",
+             "  return this;\n", "}");
+      } else if (typeName.equals("EnumSet<Media>")) {
+        puts(indent,
+             "  addMediaAttr(\"", attrName, "\", value);\n",
+             "  return this;\n", "}");
+      } else {
+        puts(indent,
+             "  addAttr(\"", attrName, "\", value);\n",
+             "  return this;\n", "}");
+      }
+    } else {
+      throwUnhandled(className, method);
+    }
+  }
+
+  String getTypeName(Type type) {
+    if (type instanceof Class<?>) {
+      return ((Class<?>)type).getSimpleName();
+    }
+    ParameterizedType pt = (ParameterizedType) type;
+    return ((Class<?>)pt.getRawType()).getSimpleName() +"<"+
+        ((Class<?>)pt.getActualTypeArguments()[0]).getSimpleName() +">";
+  }
+
+  void genFactoryMethod(String retName, String methodName, int indent) {
+    puts(indent, "\n",
+         "private <T extends __> ", retName, "<T> ", methodName,
+         "__(T e, boolean inline) {\n",
+         "  return new ", retName, "<T>(\"", StringUtils.toLowerCase(retName),
+         "\", e, opt(", !endTagOptional.contains(retName), ", inline, ",
+         retName.equals("PRE"), ")); }");
+  }
+
+  void genNewElementMethod(String className, Method method, int indent) {
+    String methodName = method.getName();
+    String retName = method.getReturnType().getSimpleName();
+    Class<?>[] params = method.getParameterTypes();
+    echo(indent, "\n",
+         "@Override\n",
+         "public ", retName, "<", className, topMode ? "> " : "<T>> ",
+         methodName, "(");
+    if (params.length == 0) {
+      puts(0, ") {");
+      puts(indent,
+           topMode ? "" : "  closeAttrs();\n",
+           "  return ", StringUtils.toLowerCase(retName), "__" + "(this, ",
+           isInline(className, retName), ");\n", "}");
+    } else if (params.length == 1) {
+      puts(0, "String selector) {");
+      puts(indent,
+           "  return setSelector(", methodName, "(), selector);\n", "}");
+    } else {
+      throwUnhandled(className, method);
+    }
+  }
+
+  boolean isInline(String container, String className) {
+    if ((container.equals("BODY") || container.equals(hamlet) ||
+         container.equals("HEAD") || container.equals("HTML")) &&
+        (className.equals("INS") || className.equals("DEL") ||
+         className.equals("SCRIPT"))) {
+      return false;
+    }
+    return inlineElements.contains(className);
+  }
+
+  void genCurElementMethod(String className, Method method, int indent) {
+    String methodName = method.getName();
+    Class<?>[] params = method.getParameterTypes();
+    if (topMode || params.length > 0) {
+      echo(indent, "\n",
+         "@Override\n",
+         "public ", className, topMode ? " " : "<T> ", methodName, "(");
+    }
+    if (params.length == 0) {
+      if (topMode) {
+        puts(0, ") {");
+        puts(indent, "  return this;\n", "}");
+      }
+    } else if (params.length == 1) {
+      if (methodName.equals("base")) {
+        puts(0, "String href) {");
+        puts(indent,
+             "  return base().$href(href).__();\n", "}");
+      } else if (methodName.equals("script")) {
+        puts(0, "String src) {");
+        puts(indent,
+             "  return setScriptSrc(script(), src).__();\n", "}");
+      } else if (methodName.equals("style")) {
+        puts(0, "Object... lines) {");
+        puts(indent,
+             "  return style().$type(\"text/css\").__(lines).__();\n", "}");
+      } else if (methodName.equals("img")) {
+        puts(0, "String src) {");
+        puts(indent,
+             "  return ", methodName, "().$src(src).__();\n", "}");
+      } else if (methodName.equals("br") || methodName.equals("hr") ||
+                 methodName.equals("col")) {
+        puts(0, "String selector) {");
+        puts(indent,
+             "  return setSelector(", methodName, "(), selector).__();\n", "}");
+      }  else if (methodName.equals("link")) {
+        puts(0, "String href) {");
+        puts(indent,
+             "  return setLinkHref(", methodName, "(), href).__();\n", "}");
+      } else if (methodName.equals("__")) {
+        if (params[0].getSimpleName().equals("Class")) {
+          puts(0, "Class<? extends SubView> cls) {");
+          puts(indent,
+               "  ", topMode ? "subView" : "_v", "(cls);\n",
+               "  return this;\n", "}");
+        } else {
+          puts(0, "Object... lines) {");
+          puts(indent,
+               "  _p(", needsEscaping(className), ", lines);\n",
+               "  return this;\n", "}");
+        }
+      } else if (methodName.equals("_r")) {
+        puts(0, "Object... lines) {");
+        puts(indent,
+             "  _p(false, lines);\n",
+             "  return this;\n", "}");
+      } else {
+        puts(0, "String cdata) {");
+        puts(indent,
+             "  return ", methodName, "().__(cdata).__();\n", "}");
+      }
+    } else if (params.length == 2) {
+      if (methodName.equals("meta")) {
+        puts(0, "String name, String content) {");
+        puts(indent,
+             "  return meta().$name(name).$content(content).__();\n", "}");
+      } else if (methodName.equals("meta_http")) {
+        puts(0, "String header, String content) {");
+        puts(indent,
+             "  return meta().$http_equiv(header).$content(content).__();\n",
+             "}");
+      } else if (methodName.equals("a")) {
+        puts(0, "String href, String anchorText) {");
+        puts(indent,
+             "  return a().$href(href).__(anchorText).__();\n", "}");
+      } else if (methodName.equals("bdo")) {
+        puts(0, "Dir dir, String cdata) {");
+        puts(indent, "  return bdo().$dir(dir).__(cdata).__();\n", "}");
+      } else if (methodName.equals("label")) {
+        puts(0, "String forId, String cdata) {");
+        puts(indent, "  return label().$for(forId).__(cdata).__();\n", "}");
+      } else if (methodName.equals("param")) {
+        puts(0, "String name, String value) {");
+        puts(indent,
+             "  return param().$name(name).$value(value).__();\n", "}");
+      } else {
+        puts(0, "String selector, String cdata) {");
+        puts(indent,
+             "  return setSelector(", methodName,
+             "(), selector).__(cdata).__();\n", "}");
+      }
+    } else if (params.length == 3) {
+      if (methodName.equals("a")) {
+        puts(0, "String selector, String href, String anchorText) {");
+        puts(indent,
+             "  return setSelector(a(), selector)",
+             ".$href(href).__(anchorText).__();\n", "}");
+      }
+    } else {
+      throwUnhandled(className, method);
+    }
+  }
+
+  static boolean needsEscaping(String eleName) {
+    return !eleName.equals("SCRIPT") && !eleName.equals("STYLE");
+  }
+
+  static void throwUnhandled(String className, Method method) {
+    throw new WebAppException("Unhandled " + className + "#" + method);
+  }
+
+  void echo(int indent, Object... args) {
+    String prev = null;
+    for (Object o : args) {
+      String s = String.valueOf(o);
+      if (!s.isEmpty() && !s.equals("\n") &&
+          (prev == null || prev.endsWith("\n"))) {
+        indent(indent);
+      }
+      prev = s;
+      out.print(s);
+      bytes += s.length();
+    }
+  }
+
+  void indent(int indent) {
+    for (int i = 0; i < indent; ++i) {
+      out.print("  ");
+      bytes += 2;
+    }
+  }
+
+  void puts(int indent, Object... args) {
+    echo(indent, args);
+    out.println();
+    ++bytes;
+  }
+
+  boolean isElement(String s) {
+    return elementRegex.matcher(s).matches();
+  }
+
+  public static void main(String[] args) throws Exception {
+    CommandLine cmd = new GnuParser().parse(opts, args);
+    if (cmd.hasOption("help")) {
+      new HelpFormatter().printHelp("Usage: hbgen [OPTIONS]", opts);
+      return;
+    }
+    // defaults
+    Class<?> specClass = HamletSpec.class;
+    Class<?> implClass = HamletImpl.class;
+    String outputClass = "HamletTmp";
+    String outputPackage = implClass.getPackage().getName();
+    if (cmd.hasOption("spec-class")) {
+      specClass = Class.forName(cmd.getOptionValue("spec-class"));
+    }
+    if (cmd.hasOption("impl-class")) {
+      implClass = Class.forName(cmd.getOptionValue("impl-class"));
+    }
+    if (cmd.hasOption("output-class")) {
+      outputClass = cmd.getOptionValue("output-class");
+    }
+    if (cmd.hasOption("output-package")) {
+      outputPackage = cmd.getOptionValue("output-package");
+    }
+    new HamletGen().generate(specClass, implClass, outputClass, outputPackage);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/38c6fa5c/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletImpl.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletImpl.java
new file mode 100644
index 0000000..995e9fb
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/hamlet2/HamletImpl.java
@@ -0,0 +1,385 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.hadoop.yarn.webapp.hamlet2;
+
+import com.google.common.base.Joiner;
+import static com.google.common.base.Preconditions.*;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+import java.io.PrintWriter;
+import java.util.EnumSet;
+import static java.util.EnumSet.*;
+import java.util.Iterator;
+
+import static org.apache.commons.lang.StringEscapeUtils.*;
+import static org.apache.hadoop.yarn.webapp.hamlet2.HamletImpl.EOpt.*;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.yarn.webapp.SubView;
+import org.apache.hadoop.yarn.webapp.WebAppException;
+
+
+/**
+ * A simple unbuffered generic hamlet implementation.
+ *
+ * Zero copy but allocation on every element, which could be
+ * optimized to use a thread-local element pool.
+ *
+ * Prints HTML as it builds. So the order is important.
+ */
+@InterfaceAudience.Private
+public class HamletImpl extends HamletSpec {
+  private static final String INDENT_CHARS = "  ";
+  private static final Splitter SS = Splitter.on('.').
+      omitEmptyStrings().trimResults();
+  private static final Joiner SJ = Joiner.on(' ');
+  private static final Joiner CJ = Joiner.on(", ");
+  static final int S_ID = 0;
+  static final int S_CLASS = 1;
+
+  int nestLevel;
+  int indents; // number of indent() called. mostly for testing.
+  private final PrintWriter out;
+  private final StringBuilder sb = new StringBuilder(); // not shared
+  private boolean wasInline = false;
+
+  /**
+   * Element options. (whether it needs end tag, is inline etc.)
+   */
+  public enum EOpt {
+    /** needs end(close) tag */
+    ENDTAG,
+    /** The content is inline */
+    INLINE,
+    /** The content is preformatted */
+    PRE
+  };
+
+  /**
+   * The base class for elements
+   * @param <T> type of the parent (containing) element for the element
+   */
+  public class EImp<T extends __> implements _Child {
+    private final String name;
+    private final T parent; // short cut for parent element
+    private final EnumSet<EOpt> opts; // element options
+
+    private boolean started = false;
+    private boolean attrsClosed = false;
+
+    EImp(String name, T parent, EnumSet<EOpt> opts) {
+      this.name = name;
+      this.parent = parent;
+      this.opts = opts;
+    }
+
+    @Override
+    public T __() {
+      closeAttrs();
+      --nestLevel;
+      printEndTag(name, opts);
+      return parent;
+    }
+
+    protected void _p(boolean quote, Object... args) {
+      closeAttrs();
+      for (Object s : args) {
+        if (!opts.contains(PRE)) {
+          indent(opts);
+        }
+        out.print(quote ? escapeHtml(String.valueOf(s))
+                        : String.valueOf(s));
+        if (!opts.contains(INLINE) && !opts.contains(PRE)) {
+          out.println();
+        }
+      }
+    }
+
+    protected void _v(Class<? extends SubView> cls) {
+      closeAttrs();
+      subView(cls);
+    }
+
+    protected void closeAttrs() {
+      if (!attrsClosed) {
+        startIfNeeded();
+        ++nestLevel;
+        out.print('>');
+        if (!opts.contains(INLINE) && !opts.contains(PRE)) {
+          out.println();
+        }
+        attrsClosed = true;
+      }
+    }
+
+    protected void addAttr(String name, String value) {
+      checkState(!attrsClosed, "attribute added after content");
+      startIfNeeded();
+      printAttr(name, value);
+    }
+
+    protected void addAttr(String name, Object value) {
+      addAttr(name, String.valueOf(value));
+    }
+
+    protected void addMediaAttr(String name, EnumSet<Media> media) {
+      // 6.13 comma-separated list
+      addAttr(name, CJ.join(media));
+    }
+
+    protected void addRelAttr(String name, EnumSet<LinkType> types) {
+      // 6.12 space-separated list
+      addAttr(name, SJ.join(types));
+    }
+
+    private void startIfNeeded() {
+      if (!started) {
+        printStartTag(name, opts);
+        started = true;
+      }
+    }
+
+    protected void _inline(boolean choice) {
+      if (choice) {
+        opts.add(INLINE);
+      } else {
+        opts.remove(INLINE);
+      }
+    }
+
+    protected void _endTag(boolean choice) {
+      if (choice) {
+        opts.add(ENDTAG);
+      } else {
+        opts.remove(ENDTAG);
+      }
+    }
+
+    protected void _pre(boolean choice) {
+      if (choice) {
+        opts.add(PRE);
+      } else {
+        opts.remove(PRE);
+      }
+    }
+  }
+
+  public class Generic<T extends __> extends EImp<T> implements PCData {
+    Generic(String name, T parent, EnumSet<EOpt> opts) {
+      super(name, parent, opts);
+    }
+
+    public Generic<T> _inline() {
+      super._inline(true);
+      return this;
+    }
+
+    public Generic<T> _noEndTag() {
+      super._endTag(false);
+      return this;
+    }
+
+    public Generic<T> _pre() {
+      super._pre(true);
+      return this;
+    }
+
+    public Generic<T> _attr(String name, String value) {
+      addAttr(name, value);
+      return this;
+    }
+
+    public Generic<Generic<T>> _elem(String name, EnumSet<EOpt> opts) {
+      closeAttrs();
+      return new Generic<Generic<T>>(name, this, opts);
+    }
+
+    public Generic<Generic<T>> elem(String name) {
+      return _elem(name, of(ENDTAG));
+    }
+
+    @Override
+    public Generic<T> __(Object... lines) {
+      _p(true, lines);
+      return this;
+    }
+
+    @Override
+    public Generic<T> _r(Object... lines) {
+      _p(false, lines);
+      return this;
+    }
+  }
+
+  public HamletImpl(PrintWriter out, int nestLevel, boolean wasInline) {
+    this.out = out;
+    this.nestLevel = nestLevel;
+    this.wasInline = wasInline;
+  }
+
+  public int nestLevel() {
+    return nestLevel;
+  }
+
+  public boolean wasInline() {
+    return wasInline;
+  }
+
+  public void setWasInline(boolean state) {
+    wasInline = state;
+  }
+
+  public PrintWriter getWriter() {
+    return out;
+  }
+
+  /**
+   * Create a root-level generic element.
+   * Mostly for testing purpose.
+   * @param <T> type of the parent element
+   * @param name of the element
+   * @param opts {@link EOpt element options}
+   * @return the element
+   */
+  public <T extends __>
+  Generic<T> root(String name, EnumSet<EOpt> opts) {
+    return new Generic<T>(name, null, opts);
+  }
+
+  public <T extends __> Generic<T> root(String name) {
+    return root(name, of(ENDTAG));
+  }
+
+  protected void printStartTag(String name, EnumSet<EOpt> opts) {
+    indent(opts);
+    sb.setLength(0);
+    out.print(sb.append('<').append(name).toString()); // for easier mock test
+  }
+
+  protected void indent(EnumSet<EOpt> opts) {
+    if (opts.contains(INLINE) && wasInline) {
+      return;
+    }
+    if (wasInline) {
+      out.println();
+    }
+    wasInline = opts.contains(INLINE) || opts.contains(PRE);
+    for (int i = 0; i < nestLevel; ++i) {
+      out.print(INDENT_CHARS);
+    }
+    ++indents;
+  }
+
+  protected void printEndTag(String name, EnumSet<EOpt> opts) {
+    if (!opts.contains(ENDTAG)) {
+      return;
+    }
+    if (!opts.contains(PRE)) {
+      indent(opts);
+    } else {
+      wasInline = opts.contains(INLINE);
+    }
+    sb.setLength(0);
+    out.print(sb.append("</").append(name).append('>').toString()); // ditto
+    if (!opts.contains(INLINE)) {
+      out.println();
+    }
+  }
+
+  protected void printAttr(String name, String value) {
+    sb.setLength(0);
+    sb.append(' ').append(name);
+    if (value != null) {
+      sb.append("=\"").append(escapeHtml(value)).append("\"");
+    }
+    out.print(sb.toString());
+  }
+
+  /**
+   * Sub-classes should override this to do something interesting.
+   * @param cls the sub-view class
+   */
+  protected void subView(Class<? extends SubView> cls) {
+    indent(of(ENDTAG)); // not an inline view
+    sb.setLength(0);
+    out.print(sb.append('[').append(cls.getName()).append(']').toString());
+    out.println();
+  }
+
+  /**
+   * Parse selector into id and classes
+   * @param selector in the form of (#id)?(.class)*
+   * @return an two element array [id, "space-separated classes"].
+   *         Either element could be null.
+   * @throws WebAppException when both are null or syntax error.
+   */
+  public static String[] parseSelector(String selector) {
+    String[] result = new String[]{null, null};
+    Iterable<String> rs = SS.split(selector);
+    Iterator<String> it = rs.iterator();
+    if (it.hasNext()) {
+      String maybeId = it.next();
+      if (maybeId.charAt(0) == '#') {
+        result[S_ID] = maybeId.substring(1);
+        if (it.hasNext()) {
+          result[S_CLASS] = SJ.join(Iterables.skip(rs, 1));
+        }
+      } else {
+        result[S_CLASS] = SJ.join(rs);
+      }
+      return result;
+    }
+    throw new WebAppException("Error parsing selector: "+ selector);
+  }
+
+  /**
+   * Set id and/or class attributes for an element.
+   * @param <E> type of the element
+   * @param e the element
+   * @param selector Haml form of "(#id)?(.class)*"
+   * @return the element
+   */
+  public static <E extends CoreAttrs> E setSelector(E e, String selector) {
+    String[] res = parseSelector(selector);
+    if (res[S_ID] != null) {
+      e.$id(res[S_ID]);
+    }
+    if (res[S_CLASS] != null) {
+      e.$class(res[S_CLASS]);
+    }
+    return e;
+  }
+
+  public static <E extends LINK> E setLinkHref(E e, String href) {
+    if (href.endsWith(".css")) {
+      e.$rel("stylesheet"); // required in html5
+    }
+    e.$href(href);
+    return e;
+  }
+
+  public static <E extends SCRIPT> E setScriptSrc(E e, String src) {
+    if (src.endsWith(".js")) {
+      e.$type("text/javascript"); // required in html4
+    }
+    e.$src(src);
+    return e;
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org