Package com.puppetlabs.geppetto.forge.util

Source Code of com.puppetlabs.geppetto.forge.util.ModulefileParser

/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*   Puppet Labs
*/
package com.puppetlabs.geppetto.forge.util;

import static com.puppetlabs.geppetto.diagnostic.Diagnostic.ERROR;
import static com.puppetlabs.geppetto.diagnostic.Diagnostic.WARNING;
import static com.puppetlabs.geppetto.forge.Forge.FORGE;
import static com.puppetlabs.geppetto.forge.Forge.PACKAGE;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jrubyparser.SourcePosition;
import org.jrubyparser.ast.FCallNode;
import org.jrubyparser.ast.FixnumNode;
import org.jrubyparser.ast.FloatNode;
import org.jrubyparser.ast.IArgumentNode;
import org.jrubyparser.ast.ListNode;
import org.jrubyparser.ast.Node;
import org.jrubyparser.ast.NodeType;
import org.jrubyparser.ast.RootNode;
import org.jrubyparser.ast.StrNode;

import com.puppetlabs.geppetto.diagnostic.Diagnostic;
import com.puppetlabs.geppetto.diagnostic.FileDiagnostic;
import com.puppetlabs.geppetto.forge.model.Dependency;
import com.puppetlabs.geppetto.forge.model.ModuleName;
import com.puppetlabs.geppetto.semver.Version;
import com.puppetlabs.geppetto.semver.VersionRange;

/**
* An Modulefile parser that produces calls with one, two, or three string
* arguments.
*/
public abstract class ModulefileParser {
  private static final String[] validProperties = new String[] {
      "name", "author", "description", "license", "project_page", "source", "summary", "version", "dependency" };

  private static Argument createArgument(Node n) {
    SourcePosition p = n.getPosition();
    String v = getString(n);
    return new Argument(p.getStartOffset(), p.getEndOffset() - p.getStartOffset(), v);
  }

  private static String getString(Node node) {
    switch(node.getNodeType()) {
      case STRNODE:
        return ((StrNode) node).getValue();
      case FIXNUMNODE:
        return Long.toString(((FixnumNode) node).getValue());
      case FLOATNODE:
        return Double.toString(((FloatNode) node).getValue());
      default:
        return null;
    }
  }

  private static boolean isValidCall(String key) {
    int idx = validProperties.length;
    while(--idx >= 0)
      if(validProperties[idx].equals(key))
        return true;
    return false;
  }

  private Diagnostic diagnostics;

  private ModuleName fullName;

  private boolean nameSeen;

  private boolean versionSeen;

  protected void addDiagnostic(int severity, SourcePosition pos, String message) {
    FileDiagnostic diag = new FileDiagnostic(severity, FORGE, message, new File(pos.getFile()));
    diag.setLineNumber(pos.getEndLine() + 1);
    diagnostics.addChild(diag);
  }

  protected void addError(SourcePosition pos, String message) {
    if(diagnostics == null)
      throw new IllegalArgumentException(message);
    addDiagnostic(ERROR, pos, message);
  }

  protected void addWarning(SourcePosition pos, String message) {
    if(diagnostics != null)
      addDiagnostic(WARNING, pos, message);
  }

  protected abstract void call(CallSymbol key, SourcePosition pos, List<Argument> arguments);

  protected Dependency createDependency(String name, String versionRequirement, SourcePosition pos) {
    Dependency dep = new DependencyWithPosition(
      pos.getStartOffset(), pos.getEndOffset() - pos.getStartOffset(), pos.getStartLine(),
      new File(pos.getFile()));
    dep.setName(createModuleName(name, true, pos));
    if(versionRequirement != null)
      try {
        dep.setVersionRequirement(VersionRange.create(versionRequirement));
      }
      catch(IllegalArgumentException e) {
        addError(pos, getBadVersionRangeMessage(e));
      }
    return dep;
  }

  protected ModuleName createModuleName(String name, boolean dependency, SourcePosition pos) {
    if(name == null)
      return null;

    name = name.trim();
    if(name.length() == 0)
      return null;

    ModuleName m = null;
    try {
      m = ModuleName.create(name, true);
    }
    catch(IllegalArgumentException e1) {
      try {
        m = ModuleName.create(name, false);
        addWarning(pos, getBadNameMessage(e1, dependency));
      }
      catch(IllegalArgumentException e2) {
        addError(pos, getBadNameMessage(e2, dependency));
      }
    }
    return m;
  }

  protected Version createVersion(String version, SourcePosition pos) {
    try {
      return Version.fromString(version);
    }
    catch(IllegalArgumentException e) {
      addError(pos, e.getMessage());
      return null;
    }
  }

  protected String getBadNameMessage(IllegalArgumentException e, boolean dependency) {
    String pfx = dependency
        ? "A dependency "
        : "A module ";
    return pfx + e.getMessage();
  }

  protected String getBadVersionRangeMessage(IllegalArgumentException e) {
    return e.getMessage();
  }

  public ModuleName getFullName() {
    return fullName;
  }

  private List<Node> getStringArguments(IArgumentNode callNode) throws IllegalArgumentException {
    Node argsNode = callNode.getArgs();
    if(!(argsNode instanceof ListNode))
      throw new IllegalArgumentException("IArgumentNode expected");
    ListNode args = (ListNode) argsNode;
    int top = args.size();
    ArrayList<Node> stringArgs = new ArrayList<Node>(top);
    for(int idx = 0; idx < top; ++idx) {
      Node argNode = args.get(idx);
      switch(argNode.getNodeType()) {
        case STRNODE:
        case FIXNUMNODE:
        case FLOATNODE:
        case NILNODE:
          stringArgs.add(argNode);
          break;
        default:
          addError(argNode.getPosition(), "Unexpected ruby code. Node type was: " + (argNode == null
              ? "null"
              : argNode.getClass().getSimpleName()));
      }
    }
    return stringArgs;
  }

  public boolean isNameSeen() {
    return nameSeen;
  }

  public boolean isVersionSeen() {
    return versionSeen;
  }

  protected void noResponse(String key, SourcePosition pos, int nargs) {
    StringBuilder bld = new StringBuilder();
    bld.append('\'');
    bld.append(key);
    bld.append("' is not a metadata property");
    if(isValidCall(key)) {
      // This is a valid property so the number of arguments
      // must be wrong.
      bld.append(" that takes ");
      if(nargs > 0)
        bld.append(nargs);
      else
        bld.append("no");
      bld.append(" argument");
      if(nargs == 0 || nargs > 1)
        bld.append('s');
    }
    addError(pos, bld.toString());
  }

  public void parseRubyAST(RootNode root, Diagnostic diagnostics) {
    this.diagnostics = diagnostics;
    nameSeen = false;
    versionSeen = false;
    fullName = null;
    File file = null;
    for(Node node : RubyParserUtils.findNodes(root.getBody(), new NodeType[] { NodeType.FCALLNODE })) {
      FCallNode call = (FCallNode) node;
      SourcePosition pos = call.getPosition();
      if(file == null)
        file = new File(pos.getFile());

      String key = call.getName();
      List<Node> args = getStringArguments(call);
      int nargs = args.size();
      if(nargs > 3 || !isValidCall(key)) {
        noResponse(key, pos, nargs);
        continue;
      }

      CallSymbol callSymbol;
      try {
        callSymbol = CallSymbol.valueOf(key);
      }
      catch(IllegalArgumentException e) {
        noResponse(key, pos, nargs);
        continue;
      }

      List<Argument> arguments;
      if(nargs == 0)
        arguments = Collections.emptyList();
      else {
        if(nargs == 1)
          arguments = Collections.singletonList(createArgument(args.get(0)));
        else {
          arguments = new ArrayList<Argument>(nargs);
          for(int idx = 0; idx < nargs; ++idx)
            arguments.add(createArgument(args.get(idx)));
        }
      }
      call(callSymbol, pos, arguments);
    }

    if(file != null) {
      if(!nameSeen || fullName != null && (fullName.getOwner() == null || fullName.getName() == null)) {
        diagnostics.addChild(new FileDiagnostic(
          ERROR, PACKAGE, "A full name (user-module) must be specified", file));
      }

      if(!versionSeen) {
        diagnostics.addChild(new FileDiagnostic(ERROR, PACKAGE, "A version must be specified", file));
      }
    }
  }

  public void setFullName(ModuleName fullName) {
    this.fullName = fullName;
  }

  public void setNameSeen() {
    nameSeen = true;
  }

  public void setVersionSeen() {
    versionSeen = true;
  }
}
TOP

Related Classes of com.puppetlabs.geppetto.forge.util.ModulefileParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.