All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jknack.handlebars.PathCompiler Maven / Gradle / Ivy

/**
 * Copyright (c) 2012-2015 Edgar Espina
 *
 * This file is part of Handlebars.java.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jknack.handlebars;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.github.jknack.handlebars.internal.PathExpressionList;
import com.github.jknack.handlebars.internal.path.DataPath;
import com.github.jknack.handlebars.internal.path.IndexedPath;
import com.github.jknack.handlebars.internal.path.ParentPath;
import com.github.jknack.handlebars.internal.path.PropertyPath;
import com.github.jknack.handlebars.internal.path.ResolveParentPath;
import com.github.jknack.handlebars.internal.path.ResolveThisPath;
import com.github.jknack.handlebars.internal.path.ThisPath;

/**
 * Compile mustache/handlebars expressions.
 *
 * @author edgar
 * @since 4.0.1.
 */
public final class PathCompiler {

  /** right parenthesis. */
  private static final char RP = ']';

  /** left parenthesis. */
  private static final char LP = '[';

  /** at symbol. */
  private static final char AT = '@';

  /** Parent traversal. */
  private static final String PARENT_PATH = "../";

  /** Parent. */
  private static final String PARENT = "..";

  /** This mustache. */
  private static final String DOT = ".";

  /** This handlebars. */
  private static final String DOT_PATH = "./";

  /** This handlebars. */
  private static final String THIS = "this";

  /** Cache with path expressions. */
  private static Map> cache = new ConcurrentHashMap<>();

  /** Split pattern. */
  private static Pattern pattern = Pattern
      .compile("((\\[[^\\[\\]]+])|(../)|([^" + Pattern.quote(DOT_PATH) + "]+))");

  /**
   * Not allowed.
   */
  private PathCompiler() {
  }

  /**
   * Split the property name by separator (except within a [] escaped blocked)
   * and create an array of it. The compiled expression will extend lookup to parent.
   *
   * @param key The property's name.
   * @return A path representation of the property (array based).
   */
  public static List compile(final String key) {
    return compile(key, true);
  }

  /**
   * Split the property name by separator (except within a [] escaped blocked)
   * and create an array of it.
   *
   * @param key The property's name.
   * @param parentScopeResolution False, if we want to restrict lookup to current scope.
   * @return A path representation of the property (array based).
   */
  public static List compile(final String key,
      final boolean parentScopeResolution) {
    boolean local = !parentScopeResolution;
    String ukey = key + local;
    List path = cache.get(ukey);
    if (path == null) {
      path = parse(key, local);
      cache.put(ukey, path);
    }
    return path;
  }

  /**
   * Split the property name by separator (except within a [] escaped blocked)
   * and create an array of it.
   *
   * @param path The property's path.
   * @param local True, if we want to restrict lookup to current scope.
   * @return A path representation of the property (array based).
   */
  private static List parse(final String path, final boolean local) {
    List resolvers = new PathExpressionList(path);

    if (THIS.equals(path) || DOT_PATH.equals(path) || DOT.equals(path)) {
      resolvers.add(new ResolveThisPath(path));
      return resolvers;
    }
    if (PARENT.equals(path)) {
      resolvers.add(new ResolveParentPath());
      return resolvers;
    }
    if (path.startsWith(PARENT_PATH)) {
      resolvers.add(new ParentPath());
      resolvers.addAll(parse(path.substring(PARENT_PATH.length()), local));
      return resolvers;
    }
    if (path.startsWith(DOT_PATH)) {
      resolvers.add(new ThisPath(DOT_PATH));
      resolvers.addAll(parse(path.substring(DOT_PATH.length()), local));
      return resolvers;
    }
    Matcher matcher = pattern.matcher(path);
    boolean data = false;
    while (matcher.find()) {
      String key = matcher.group(1);
      if (THIS.equals(key)) {
        resolvers.add(new ThisPath(key));
      } else if (PARENT_PATH.equals(key)) {
        resolvers.add(new ParentPath());
      } else if (key.charAt(0) == AT) {
        if (key.length() == 1) {
          data = true;
        } else {
          resolvers.add(new DataPath(key));
        }
      } else {
        if (key.charAt(0) == LP && key.charAt(key.length() - 1) == RP) {
          key = key.substring(1, key.length() - 1);
        }
        try {
          resolvers.add(new IndexedPath(Integer.parseInt(key), key, local));
        } catch (NumberFormatException ex) {
          if (data) {
            resolvers.add(new DataPath(AT + key));
          } else {
            resolvers.add(new PropertyPath(key, local));
          }
        }
      }
    }
    return resolvers;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy