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

org.jamon.parser.CallParser Maven / Gradle / Ivy

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.jamon.parser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jamon.api.Location;
import org.jamon.compiler.ParserErrorImpl;
import org.jamon.compiler.ParserErrorsImpl;
import org.jamon.node.AbsolutePathNode;
import org.jamon.node.AbstractCallNode;
import org.jamon.node.AbstractComponentCallNode;
import org.jamon.node.AbstractParamsNode;
import org.jamon.node.AbstractPathNode;
import org.jamon.node.ChildCallNode;
import org.jamon.node.FragmentCallNode;
import org.jamon.node.GenericCallParam;
import org.jamon.node.MultiFragmentCallNode;
import org.jamon.node.NamedFragmentNode;
import org.jamon.node.NamedParamNode;
import org.jamon.node.NamedParamsNode;
import org.jamon.node.NoParamsNode;
import org.jamon.node.ParamNameNode;
import org.jamon.node.ParamValueNode;
import org.jamon.node.SimpleCallNode;
import org.jamon.node.UnnamedFragmentNode;
import org.jamon.node.UnnamedParamsNode;

public class CallParser extends AbstractParser {
  public static final String INVALID_CALL_TARGET_ERROR = "Invalid call target";
  public static final String MISSING_CALL_CLOSE_ERROR = "Expecting '&>'";
  public static final String UNEXPECTED_IN_MULTI_FRAG_ERROR =
    "Expecting either '<|identifier>' or ''";
  public static final String MISSING_ARG_ARROW_ERROR =
    "Expecting '=' or '=>' to separate param name and value";
  public static final String GENERIC_ERROR = "Malformed call tag";
  public static final String PARAM_VALUE_EOF_ERROR =
    "Reached end of file while reading parameter value";
  public static final String FRAGMENTS_EOF_ERROR =
    "Reached end of file while reading call fragments; '' Expected";
  public static final String MISSING_GENERIC_PARAM_CLOSE_ERROR = "Expecing ',' or '>'";

  public CallParser(
    PositionalPushbackReader reader, ParserErrorsImpl errors, Location callStartLocation)
  throws IOException {
    super(reader, errors);
    try {
      if (readChar('|')) {
        if (readChar('|')) {
          callNode = parseNamedFragmentCall(callStartLocation);
        }
        else {
          callNode = parseUnnamedFragmentCall(callStartLocation);
        }
        addGenericParams();
      }
      else {
        soakWhitespace();
        if (readChar('*')) {
          parseChildCall(callStartLocation);
        }
        else {
          AbstractPathNode path = parsePath();
          parseGenericParams();
          callNode = new SimpleCallNode(callStartLocation, path, parseParams());
          addGenericParams();
        }
      }
    }
    catch (ParserErrorImpl e) {
      addError(e);
      callNode = new SimpleCallNode(callStartLocation,
          new AbsolutePathNode(callStartLocation), new NoParamsNode(reader.getLocation()));
    }
  }

  private void parseChildCall(Location callStartLocation) throws IOException, ParserErrorImpl {
    Location callTargetLocation = reader.getLocation();
    if (checkToken("CHILD")) {
      soakWhitespace();
      Location endLocation = reader.getNextLocation();
      if (checkToken("&>")) {
        callNode = new ChildCallNode(callStartLocation);
      }
      else {
        throw new ParserErrorImpl(endLocation, MISSING_CALL_CLOSE_ERROR);
      }
    }
    else {
      throw new ParserErrorImpl(callTargetLocation, INVALID_CALL_TARGET_ERROR);
    }
  }

  private AbstractComponentCallNode parseNamedFragmentCall(Location callStartLocation)
  throws IOException, ParserErrorImpl {
    soakWhitespace();
    AbstractPathNode path = parsePath();
    parseGenericParams();
    MultiFragmentCallNode callNode = new MultiFragmentCallNode(callStartLocation, path,
        parseParams());
    Location fragmentsStart = reader.getNextLocation();
    while (true) {
      soakWhitespace();
      Location fragmentStart = reader.getNextLocation();
      int c = reader.read();
      if (c == '<') {
        switch (reader.read()) {
          case '|':
            String name = readIdentifier(true);
            if (readChar('>')) {
              NamedFragmentNode fragmentNode = new NamedFragmentNode(fragmentStart, name);
              new NamedFragmentParser(fragmentNode, reader, errors).parse();
              callNode.addFragment(fragmentNode);
            }
            else {
              throw new ParserErrorImpl(reader.getLocation(), UNEXPECTED_IN_MULTI_FRAG_ERROR);
            }
            break;
          case '/':
            if (readChar('&') && readChar('>')) {
              return callNode;
            }
            else {
              throw new ParserErrorImpl(reader.getLocation(), UNEXPECTED_IN_MULTI_FRAG_ERROR);
            }
          default:
            throw new ParserErrorImpl(reader.getLocation(), UNEXPECTED_IN_MULTI_FRAG_ERROR);
        }
      }
      else if (c >= 0) {
        throw new ParserErrorImpl(reader.getLocation(), UNEXPECTED_IN_MULTI_FRAG_ERROR);
      }
      else {
        throw new ParserErrorImpl(fragmentsStart, FRAGMENTS_EOF_ERROR);
      }
    }
  }

  private FragmentCallNode parseUnnamedFragmentCall(Location callStartLocation) throws IOException,
    ParserErrorImpl {
    soakWhitespace();
    AbstractPathNode path = parsePath();
    parseGenericParams();
    AbstractParamsNode params = parseParams();
    return new FragmentCallNode(callStartLocation, path, params, new UnnamedFragmentParser(
        new UnnamedFragmentNode(reader.getNextLocation()), reader, errors).parse().getRootNode());
  }

  private AbstractParamsNode parseParams() throws IOException, ParserErrorImpl {
    soakWhitespace();
    reader.markNodeEnd();
    int c = reader.read();
    switch (c) {
      case '&':
        if (reader.read() == '>') {
          return new NoParamsNode(reader.getCurrentNodeLocation());
        }
        else {
          throw new ParserErrorImpl(reader.getLocation(), GENERIC_ERROR);
        }
      case ';':
        return parseNamedParams();
      case ':':
        return parseUnnamedParams();
      default:
        throw new ParserErrorImpl(reader.getLocation(), GENERIC_ERROR);
    }
  }

  private static class ParamValueEndDetector implements TagEndDetector {
    public boolean noMoreParams() {
      return noMoreParams;
    }

    @Override
    public int checkEnd(final char character) {
      if (character == '&') {
        seenAmpersand = true;
        return 0;
      }
      else if (character == '>' && seenAmpersand) {
        noMoreParams = true;
        return 2;
      }
      else if (character == ';') {
        return 1;
      }
      else {
        seenAmpersand = false;
        return 0;
      }
    }

    @Override
    public ParserErrorImpl getEofError(Location startLocation) {
      return new ParserErrorImpl(startLocation, PARAM_VALUE_EOF_ERROR);
    }

    @Override
    public void resetEndMatch() {
      seenAmpersand = false;
    }

    private boolean noMoreParams = false;
    private boolean seenAmpersand = false;
  }

  private NamedParamsNode parseNamedParams() throws ParserErrorImpl, IOException {
    NamedParamsNode params = new NamedParamsNode(reader.getLocation());
    ParamValueEndDetector endDetector = new ParamValueEndDetector();
    while (true) {
      soakWhitespace();
      if (readChar('&')) {
        if (readChar('>')) {
          return params;
        }
        else {
          throw new ParserErrorImpl(reader.getCurrentNodeLocation(), GENERIC_ERROR);
        }
      }
      Location nameLoc = reader.getNextLocation();
      String name = readIdentifier(true);
      readArrow();
      Location javaLoc = reader.getNextLocation();
      params.addParam(new NamedParamNode(nameLoc, new ParamNameNode(nameLoc, name),
          new ParamValueNode(javaLoc, readJava(javaLoc, endDetector))));
      if (endDetector.noMoreParams()) {
        return params;
      }
    }
  }

  private UnnamedParamsNode parseUnnamedParams() throws ParserErrorImpl, IOException {
    UnnamedParamsNode params = new UnnamedParamsNode(reader.getLocation());
    ParamValueEndDetector endDetector = new ParamValueEndDetector();
    while (true) {
      soakWhitespace();
      if (readChar('&')) {
        if (readChar('>')) {
          return params;
        }
        else {
          throw new ParserErrorImpl(reader.getCurrentNodeLocation(), GENERIC_ERROR);
        }
      }
      Location javaLoc = reader.getNextLocation();
      params.addValue(new ParamValueNode(javaLoc, readJava(javaLoc, endDetector)));
      if (endDetector.noMoreParams()) {
        return params;
      }
    }
  }

  private void parseGenericParams() throws ParserErrorImpl, IOException {
    genericParams = new ArrayList();
    if (readChar('<')) {
      do {
        soakWhitespace();
        Location location = reader.getNextLocation();
        genericParams.add(new GenericCallParam(location, new ClassNameParser(location, reader,
            errors).getType()));
        soakWhitespace();
      }
      while (readChar(','));
      if (!readChar('>')) {
        throw new ParserErrorImpl(reader.getNextLocation(), MISSING_GENERIC_PARAM_CLOSE_ERROR);
      }
    }
  }

  private void addGenericParams() {
    AbstractComponentCallNode componentCallNode = (AbstractComponentCallNode) callNode;
    for (GenericCallParam param : genericParams) {
      componentCallNode.addGenericParam(param);
    }
  }

  private void readArrow() throws ParserErrorImpl, IOException {
    soakWhitespace();
    if (!readChar('=')) {
      throw new ParserErrorImpl(reader.getNextLocation(), MISSING_ARG_ARROW_ERROR);
    }
    readChar('>'); // support old-style syntax
    soakWhitespace();
  }

  public static void main(String[] args) {}

  public AbstractCallNode getCallNode() {
    return callNode;
  }

  private AbstractCallNode callNode;
  private List genericParams = null;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy