Пример #1
0
  @Test
  public void setResolver() throws IOException {
    ValueResolver resolver =
        new ValueResolver() {

          @Override
          public Object resolve(final Object context) {
            return 1;
          }

          @Override
          public Object resolve(final Object context, final String name) {
            return 1;
          }

          @Override
          public Set<Entry<String, Object>> propertySet(final Object context) {
            return null;
          }
        };

    Map<String, Object> hash = new HashMap<>();
    hash.put("foo", "bar");
    Context ctx = Context.newBuilder(hash).resolver(resolver).build();
    assertEquals(1, ctx.get("foo"));
    assertEquals(1, ctx.get("bar"));
  }
 /**
  * Creates a child context.
  *
  * @param parent The parent context. Required.
  * @param model The target value. Resolved as '.' or 'this' inside templates. Required.
  * @return A child context.
  */
 private static Context child(final Context parent, final Object model) {
   notNull(parent, "A parent context is required.");
   Context child = new Context(model);
   child.extendedContext = new Context(new HashMap<String, Object>());
   child.parent = parent;
   child.data = parent.data;
   return child;
 }
Пример #3
0
 /**
  * Creates a root context.
  *
  * @param model The target value. Resolved as '.' or 'this' inside templates. Required.
  * @return A root context.
  */
 private static Context root(final Object model) {
   Context root = new Context(model);
   root.extendedContext = new Context(new HashMap<String, Object>());
   root.parent = null;
   root.storage = new HashMap<String, Object>();
   root.storage.put(PARTIALS, new HashMap<String, Template>());
   return root;
 }
 /**
  * Creates a root context.
  *
  * @param model The target value. Resolved as '.' or 'this' inside templates. Required.
  * @return A root context.
  */
 private static Context root(final Object model) {
   Context root = new Context(model);
   root.extendedContext = new Context(new HashMap<String, Object>());
   root.parent = null;
   root.data = new HashMap<String, Object>();
   root.data.put(PARTIALS, new HashMap<String, Template>());
   root.data.put(INVOCATION_STACK, new LinkedList<TemplateSource>());
   return root;
 }
Пример #5
0
  @SuppressWarnings("unchecked")
  @Override
  protected void merge(final Context context, final Writer writer) throws IOException {
    if (body == null) {
      return;
    }
    final String helperName;
    Helper<Object> helper = helper(name);
    Template template = body;
    final Object childContext;
    Context currentScope = context;
    if (helper == null) {
      childContext = transform(context.get(name));
      if (inverted) {
        helperName = UnlessHelper.NAME;
      } else if (childContext instanceof Iterable) {
        helperName = EachHelper.NAME;
      } else if (childContext instanceof Boolean) {
        helperName = IfHelper.NAME;
      } else if (childContext instanceof Lambda) {
        helperName = WithHelper.NAME;
        template =
            Lambdas.compile(
                handlebars,
                (Lambda<Object, Object>) childContext,
                context,
                template,
                startDelimiter,
                endDelimiter);
      } else {
        helperName = WithHelper.NAME;
        currentScope = Context.newContext(context, childContext);
      }
      // A built-in helper might be override it.
      helper = handlebars.helper(helperName);
    } else {
      helperName = name;
      childContext = transform(determineContext(context));
    }
    Options options =
        new Options.Builder(handlebars, helperName, TagType.SECTION, currentScope, template)
            .setInverse(inverse == null ? Template.EMPTY : inverse)
            .setParams(params(currentScope))
            .setHash(hash(context))
            .build();
    options.data(Context.PARAM_SIZE, this.params.size());

    CharSequence result = helper.apply(childContext, options);
    if (!isEmpty(result)) {
      writer.append(result);
    }
  }
Пример #6
0
 @Override
 public CharSequence apply(final Object context, final Options options) throws IOException {
   if (context == null) {
     return null;
   }
   if (options.params.length <= 0) {
     return context.toString();
   }
   Context ctx = Context.newBuilder(options.context, context).build();
   Object lookup = ctx.get(options.param(0).toString());
   if (lookup == null) {
     return null;
   }
   return lookup.toString();
 }
  private CharSequence iterableContext(Iterable<?> context, Options options) throws IOException {
    if (options.isFalsy(context)) {
      return options.inverse();
    }

    StringBuilder buffer = new StringBuilder();

    Iterator<?> iterator = reverse(context);
    int index = 0;
    Context parent = options.context;
    while (iterator.hasNext()) {
      Object element = iterator.next();

      boolean first = index == 0;
      boolean even = index % 2 == 0;
      boolean last = !iterator.hasNext();

      Context current =
          Context.newContext(parent, element)
              .data("index", index)
              .data("first", first ? "first" : "")
              .data("last", last ? "last" : "")
              .data("odd", even ? "" : "odd")
              .data("even", even ? "even" : "");
      buffer.append(options.fn(current));

      index++;
    }

    return buffer;
  }
 /**
  * Build a context stack.
  *
  * @return A new context stack.
  */
 public Context build() {
   if (context.resolver == null) {
     if (context.parent != null) {
       // Set resolver from parent.
       context.resolver = context.parent.resolver;
     } else {
       // Set default value resolvers: Java Bean like and Map resolvers.
       context.setResolver(new CompositeValueResolver(ValueResolver.VALUE_RESOLVERS));
     }
     // Expand resolver to the extended context.
     if (context.extendedContext != null) {
       context.extendedContext.resolver = context.resolver;
     }
   }
   return context;
 }
Пример #9
0
  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    File basedir = new File(prefix);
    TemplateLoader loader = new FileTemplateLoader(basedir, suffix);
    Handlebars handlebars = new Handlebars(loader);
    File output = new File(this.output);
    PrintWriter writer = null;
    boolean error = true;
    InputStream runtimeIS = getClass().getResourceAsStream("/handlebars.runtime.js");
    try {
      writer = new PrintWriter(output);
      if (includeRuntime) {
        IOUtil.copy(runtimeIS, writer);
      }
      List<File> files = FileUtils.getFiles(basedir, "/**/*" + suffix, null);
      getLog().info("Compiling templates...");
      getLog().debug("Options:");
      getLog().debug("  output: " + output);
      getLog().debug("  prefix: " + prefix);
      getLog().debug("  suffix: " + suffix);
      getLog().debug("  minimize: " + minimize);
      getLog().debug("  includeRuntime: " + includeRuntime);

      Context nullContext = Context.newContext(null);
      for (File file : files) {
        String templateName = file.getPath().replace(prefix, "").replace(suffix, "");
        if (templateName.startsWith("/")) {
          templateName = templateName.substring(1);
        }
        getLog().debug("compiling: " + templateName);
        Template template = handlebars.compileInline("{{precompile \"" + templateName + "\"}}");
        Options opts = new Options.Builder(handlebars, TagType.VAR, nullContext, template).build();

        writer.append("// Source: ").append(file.getPath()).append("\n");
        writer.append(PrecompileHelper.INSTANCE.apply(templateName, opts)).append("\n\n");
      }
      writer.flush();
      IOUtil.close(writer);
      if (minimize) {
        minimize(output);
      }
      if (files.size() > 0) {
        getLog().info("  templates were saved in: " + output);
        error = false;
      } else {
        getLog().warn("  no templates were found");
      }
    } catch (IOException ex) {
      throw new MojoFailureException("Can't scan directory " + basedir, ex);
    } finally {
      IOUtil.close(runtimeIS);
      IOUtil.close(writer);
      if (error) {
        output.delete();
      }
    }
  }
  private CharSequence hashContext(Object context, Options options) throws IOException {
    StringBuilder buffer = new StringBuilder();

    Context parent = options.context;
    Iterator<Map.Entry<String, Object>> iterator = reverse(options.propertySet(context));
    while (iterator.hasNext()) {
      Map.Entry<String, Object> entry = iterator.next();
      Context current = Context.newContext(parent, entry.getValue()).data("key", entry.getKey());
      buffer.append(options.fn(current));
    }

    return buffer;
  }
Пример #11
0
 public EngineHelper(Node node) {
   HashMap contextVariables = new HashMap();
   contextVariables.put("node", node);
   this.node = node;
   this.context =
       Context.newBuilder(contextVariables)
           .resolver(
               MethodValueResolver.INSTANCE,
               MapValueResolver.INSTANCE,
               JavaBeanValueResolver.INSTANCE,
               FieldValueResolver.INSTANCE)
           .build();
 }
Пример #12
0
 /** Destroy this context by cleaning up instance attributes. */
 public void destroy() {
   model = null;
   if (parent == null) {
     // Root context is the owner of the storage.
     if (data != null) {
       data.clear();
     }
   }
   if (extendedContext != null) {
     extendedContext.destroy();
   }
   parent = null;
   resolver = null;
   data = null;
 }
Пример #13
0
 /** Destroy this context by cleaning up instance attributes. */
 public void destroy() {
   model = null;
   if (parent == null) {
     // Root context are owner of the storage.
     if (storage != null) {
       storage.clear();
     }
   }
   if (extendedContext != null) {
     extendedContext.destroy();
   }
   parent = null;
   resolver = null;
   storage = null;
 }
Пример #14
0
 /**
  * Lookup the given key inside the context stack.
  *
  * <ul>
  *   <li>Objects and hashes should be pushed onto the context stack.
  *   <li>All elements on the context stack should be accessible.
  *   <li>Multiple sections per template should be permitted.
  *   <li>Failed context lookups should be considered falsey.
  *   <li>Dotted names should be valid for Section tags.
  *   <li>Dotted names that cannot be resolved should be considered falsey.
  *   <li>Dotted Names - Context Precedence: Dotted names should be resolved against former
  *       resolutions.
  * </ul>
  *
  * @param key The object key.
  * @return The value associated to the given key or <code>null</code> if no value is found.
  */
 public Object get(final String key) {
   if (".".equals(key) || "this".equals(key)) {
     return model;
   }
   if (key.startsWith("../")) {
     return parent == null ? null : parent.get(key.substring("../".length()));
   }
   String[] path = toPath(key);
   Object value = get(path);
   if (value == null) {
     // No luck, check the extended context.
     value = get(extendedContext, key);
     // No luck, but before checking at the parent scope we need to check for
     // the 'this' qualifier. If present, no look up will be done.
     if (value == null && !path[0].equals("this")) {
       value = get(parent, key);
     }
   }
   return value == NULL ? null : value;
 }
Пример #15
0
 /**
  * Lookup the given key inside the context stack.
  *
  * <ul>
  *   <li>Objects and hashes should be pushed onto the context stack.
  *   <li>All elements on the context stack should be accessible.
  *   <li>Multiple sections per template should be permitted.
  *   <li>Failed context lookups should be considered falsey.
  *   <li>Dotted names should be valid for Section tags.
  *   <li>Dotted names that cannot be resolved should be considered falsey.
  *   <li>Dotted Names - Context Precedence: Dotted names should be resolved against former
  *       resolutions.
  * </ul>
  *
  * @param key The object key.
  * @return The value associated to the given key or <code>null</code> if no value is found.
  */
 public Object get(final String key) {
   // '.' or 'this'
   if (MUSTACHE_THIS.equals(key) || THIS.equals(key)) {
     return model;
   }
   // '..'
   if (key.equals(PARENT)) {
     return parent == null ? null : parent.model;
   }
   // '../'
   if (key.startsWith(PARENT_ATTR)) {
     return parent == null ? null : parent.get(key.substring(PARENT_ATTR.length()));
   }
   String[] path = toPath(key);
   Object value = get(path);
   if (value == null) {
     // No luck, check the extended context.
     value = get(extendedContext, key);
     // No luck, check the data context.
     if (value == null && data != null) {
       String dataKey = key.charAt(0) == '@' ? key.substring(1) : key;
       // simple data keys will be resolved immediately, complex keys need to go down and using a
       // new context.
       value = data.get(dataKey);
       if (value == null && path.length > 1) {
         // for complex keys, a new data context need to be created per invocation,
         // bc data might changes per execution.
         Context dataContext =
             Context.newBuilder(data).resolver(MapValueResolver.INSTANCE).build();
         // don't extend the lookup further.
         dataContext.data = null;
         value = dataContext.get(dataKey);
         // destroy it!
         dataContext.destroy();
       }
     }
     // No luck, but before checking at the parent scope we need to check for
     // the 'this' qualifier. If present, no look up will be done.
     if (value == null && !path[0].equals(THIS)) {
       value = get(parent, key);
     }
   }
   return value == NULL ? null : value;
 }
Пример #16
0
 /**
  * Combine all the map entries into the context stack.
  *
  * @param model The model data.
  * @return This builder.
  */
 public Builder combine(final Map<String, Object> model) {
   context.combine(model);
   return this;
 }
Пример #17
0
 /**
  * Set the value resolver and propagate it to the extendedContext.
  *
  * @param resolver The value resolver.
  */
 private void setResolver(final ValueResolver resolver) {
   this.resolver = resolver;
   extendedContext.resolver = resolver;
 }
Пример #18
0
 /**
  * Look for the specified key in an external context.
  *
  * @param external The external context.
  * @param key The associated key.
  * @return The associated value or null if not found.
  */
 private Object get(final Context external, final String key) {
   return external == null ? null : external.get(key);
 }
Пример #19
0
  @Override
  protected void doExecute() throws Exception {
    notNull(bundle, "The bundle's name parameter is required.");
    notNull(output, "The output parameter is required.");

    Handlebars handlebars = new Handlebars();
    Context context = Context.newContext(null);
    URL[] classpath = projectClasspath();

    new File(output).mkdirs();

    getLog().info("Converting bundles...");
    getLog().debug("Options:");
    getLog().debug("  output: " + output);
    getLog().debug("  merge: " + merge);
    getLog().debug("  amd: " + amd);
    getLog().debug("  classpath: " + join(classpath, File.pathSeparator));

    StringBuilder buffer = new StringBuilder();

    // 1. find all the bundles
    List<File> bundles = bundles(this.bundle, classpath);

    // 2. hash for i18njs helper
    Map<String, Object> hash = new HashMap<String, Object>();
    hash.put("wrap", false);
    if (classpath.length > 0) {
      hash.put("classLoader", new URLClassLoader(classpath, getClass().getClassLoader()));
    }
    // 3. get the base name from the bundle
    String basename = FileUtils.removePath(this.bundle.replace(".", "/"));

    Collections.sort(bundles);
    for (File bundle : bundles) {
      // bundle name
      String bundleName = FileUtils.removeExtension(bundle.getName());
      getLog().debug("converting: " + bundle.getName());
      // extract locale from bundle name
      String locale = bundleName.substring(basename.length());
      if (locale.startsWith("_")) {
        locale = locale.substring(1);
      }
      // set bundle name
      hash.put("bundle", this.bundle);

      Options options =
          new Options.Builder(handlebars, TagType.VAR, context, Template.EMPTY)
              .setHash(hash)
              .build();
      // convert to JS
      buffer.append(I18nHelper.i18nJs.apply(locale, options));

      if (!merge) {
        FileUtils.fileWrite(
            new File(output, bundleName + ".js"), encoding, wrap(bundleName, buffer, amd));
        buffer.setLength(0);
        getLog().debug("  => " + bundleName + ".js");
      } else {
        buffer.append("\n");
      }
    }
    if (merge && buffer.length() > 0) {
      FileUtils.fileWrite(new File(output, basename + ".js"), wrap(basename, buffer, amd));
      getLog().debug("  =>" + basename + ".js");
    }
  }
Пример #20
0
 /**
  * Combine the given model using the specified name.
  *
  * @param name The variable's name. Required.
  * @param model The model data.
  * @return This builder.
  */
 public Builder combine(final String name, final Object model) {
   context.combine(name, model);
   return this;
 }
Пример #21
0
 /**
  * Creates a new context builder.
  *
  * @param model The model data.
  */
 private Builder(final Object model) {
   context = Context.root(model);
 }
Пример #22
0
 /**
  * Creates a new context builder.
  *
  * @param parent The parent context. Required.
  * @param model The model data.
  */
 private Builder(final Context parent, final Object model) {
   context = Context.child(parent, model);
 }
Пример #23
0
 /**
  * Set the value resolvers to use.
  *
  * @param resolvers The value resolvers. Required.
  * @return This builder.
  */
 public Builder resolver(final ValueResolver... resolvers) {
   notEmpty(resolvers, "At least one value-resolver must be present.");
   context.setResolver(new CompositeValueResolver(resolvers));
   return this;
 }
Пример #24
0
 @Override
 Object doParse(final Context scope, final Object param) {
   return scope.get((String) param);
 }