Package com.puppetlabs.geppetto.ruby

Source Code of com.puppetlabs.geppetto.ruby.RubyHelper

/**
* 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.ruby;

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.puppetlabs.geppetto.pp.pptp.Function;
import com.puppetlabs.geppetto.pp.pptp.ITargetElementContainer;
import com.puppetlabs.geppetto.pp.pptp.MetaType;
import com.puppetlabs.geppetto.pp.pptp.MetaVariable;
import com.puppetlabs.geppetto.pp.pptp.NameSpace;
import com.puppetlabs.geppetto.pp.pptp.PPTPFactory;
import com.puppetlabs.geppetto.pp.pptp.Parameter;
import com.puppetlabs.geppetto.pp.pptp.Property;
import com.puppetlabs.geppetto.pp.pptp.PuppetDistribution;
import com.puppetlabs.geppetto.pp.pptp.TPVariable;
import com.puppetlabs.geppetto.pp.pptp.TargetEntry;
import com.puppetlabs.geppetto.pp.pptp.Type;
import com.puppetlabs.geppetto.pp.pptp.TypeFragment;
import com.puppetlabs.geppetto.ruby.spi.IRubyIssue;
import com.puppetlabs.geppetto.ruby.spi.IRubyParseResult;
import com.puppetlabs.geppetto.ruby.spi.IRubyServices;
import com.puppetlabs.geppetto.ruby.spi.IRubyServicesFactory;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
* Provides access to ruby parsing / information services. If an implementation
* is not available, a mock service is used - this can be checked with {@link #isRubyServicesAvailable()}. The mock service will provide an empty
* parse result (i.e. "no errors or warning"), and will return empty results for
* information.
*
* The caller can then adjust how to deal with service not being present.
*
* To use the RubyHelper, a call must be made to {@link #setUp()}, then followed
* by a series of requests to parse or get information.
*
*/
public class RubyHelper {

  private static class MockRubyServices implements IRubyServices {
    private static class EmptyParseResult implements IRubyParseResult {
      private static final List<IRubyIssue> emptyIssues = Collections.emptyList();

      @Override
      public List<IRubyIssue> getIssues() {
        return emptyIssues;
      }

      @Override
      public boolean hasErrors() {
        return false;
      }

      @Override
      public boolean hasIssues() {
        return false;
      }

    }

    private static final List<PPFunctionInfo> emptyFunctionInfo = Collections.emptyList();

    private static final List<PPTypeInfo> emptyTypeInfo = Collections.emptyList();

    private static final IRubyParseResult emptyParseResult = new EmptyParseResult();

    @Override
    public List<PPFunctionInfo> getFunctionInfo(File file) throws IOException {
      return emptyFunctionInfo;
    }

    @Override
    public List<PPFunctionInfo> getFunctionInfo(String fileName, Reader reader) {
      return emptyFunctionInfo;
    }

    @Override
    public List<PPFunctionInfo> getLogFunctions(File file) throws IOException, RubySyntaxException {
      return emptyFunctionInfo;
    }

    @Override
    public PPTypeInfo getMetaTypeProperties(File file) throws IOException, RubySyntaxException {
      return emptyTypeInfo.get(0);
    }

    @Override
    public PPTypeInfo getMetaTypeProperties(String fileName, Reader reader) {
      return emptyTypeInfo.get(0);
    }

    @Override
    public Map<String, String> getRakefileTaskDescriptions(File file) {
      return Maps.newHashMap(); // empty map - can't discover anything about rakefiles
    }

    @Override
    public List<PPTypeInfo> getTypeInfo(File file) throws IOException {
      return emptyTypeInfo;
    }

    @Override
    public List<PPTypeInfo> getTypeInfo(String fileName, Reader reader) throws IOException, RubySyntaxException {
      return Collections.emptyList();
    }

    @Override
    public List<PPTypeInfo> getTypePropertiesInfo(File file) throws IOException, RubySyntaxException {
      return emptyTypeInfo;
    }

    @Override
    public List<PPTypeInfo> getTypePropertiesInfo(String fileName, Reader reader) throws IOException,
        RubySyntaxException {
      return emptyTypeInfo;
    }

    @Override
    public boolean isMockService() {
      return true;
    }

    @Override
    public IRubyParseResult parse(File file) throws IOException {
      return emptyParseResult;
    }

    @Override
    public IRubyParseResult parse(String path, Reader reader) throws IOException {
      return emptyParseResult;
    }

    @Override
    public void setUp() {
      // do nothing
    }

    @Override
    public void tearDown() {
      // do nothing
    }

  }

  private IRubyServices rubyProvider;

  private static final FilenameFilter rbFileFilter = new FilenameFilter() {

    @Override
    public boolean accept(File dir, String name) {
      return name.endsWith(".rb");
    }

  };

  private static final FileFilter dirFilter = new FileFilter() {

    @Override
    public boolean accept(File pathname) {
      return pathname.isDirectory();
    }

  };

  private static IRubyServicesFactory rubyProviderFactory = null;

  public static void setRubyServicesFactory(IRubyServicesFactory factory) {
    rubyProviderFactory = factory;
  }

  private TPVariable addTPVariable(ITargetElementContainer container, String name, String documentation,
      boolean deprecated) {
    TPVariable var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName(name);
    var.setDocumentation(documentation);
    var.setAssignable(false);
    var.setDeprecated(deprecated);
    container.getContents().add(var);
    return var;
  }

  private String[] extractVersionFromName(String name) {
    String[] result = new String[] { name, "" };
    int lastHypen = name.lastIndexOf('-');
    if(lastHypen == -1)
      return result;
    result[0] = name.substring(0, lastHypen);
    result[1] = name.substring(lastHypen + 1);
    return result;

  }

  private List<Function> functionInfoToFunction(List<PPFunctionInfo> functionInfos) {
    List<Function> result = Lists.newArrayList();
    for(PPFunctionInfo info : functionInfos) {
      Function pptpFunc = PPTPFactory.eINSTANCE.createFunction();
      pptpFunc.setName(info.getFunctionName());
      pptpFunc.setRValue(info.isRValue());
      pptpFunc.setDocumentation(info.getDocumentation());
      result.add(pptpFunc);
    }
    return result;

  }

  /**
   * Returns a list of custom PP parser functions from the given .rb file. The
   * returned list is empty if no function could be found.
   *
   * @param file
   * @return
   * @throws IOException
   *             - if there are errors reading the file
   * @throws IllegalStateException
   *             - if setUp was not called
   */
  public List<PPFunctionInfo> getFunctionInfo(File file) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getFunctionInfo(File).");
    if(file == null)
      throw new IllegalArgumentException("Given file is null - JRubyService.getFunctionInfo");
    if(!file.exists())
      throw new FileNotFoundException(file.getPath());

    return rubyProvider.getFunctionInfo(file);

  }

  public List<PPFunctionInfo> getFunctionInfo(String fileName, Reader reader) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getFunctionInfo(File).");
    if(fileName == null)
      throw new IllegalArgumentException("Given filename is null");
    if(reader == null)
      throw new IllegalArgumentException("Given reader is null");

    return rubyProvider.getFunctionInfo(fileName, reader);

  }

  public PPTypeInfo getMetaTypeInfo(File file) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getTypeInfo(File).");
    if(file == null)
      throw new IllegalArgumentException("Given file is null - JRubyService.getTypeInfo");
    if(!file.exists())
      throw new FileNotFoundException(file.getPath());
    return rubyProvider.getMetaTypeProperties(file);

  }

  public PPTypeInfo getMetaTypeInfo(String fileName, Reader reader) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before calling this method");
    if(fileName == null)
      throw new IllegalArgumentException("Given fileName is null");
    if(reader == null)
      throw new IllegalArgumentException("Given reader is null");
    return rubyProvider.getMetaTypeProperties(fileName, reader);
  }

  public Map<String, String> getRakefileTaskDescriptions(File file) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before calling this method.");
    if(file == null)
      throw new IllegalArgumentException("Given file is null");
    if(!file.exists())
      throw new FileNotFoundException(file.getPath());

    return rubyProvider.getRakefileTaskDescriptions(file);

  }

  public List<PPTypeInfo> getTypeFragments(File file) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getTypeInfo(File).");
    if(file == null)
      throw new IllegalArgumentException("Given file is null - JRubyService.getTypeInfo");
    if(!file.exists())
      throw new FileNotFoundException(file.getPath());
    return rubyProvider.getTypePropertiesInfo(file);
  }

  public List<PPTypeInfo> getTypeFragments(String fileName, Reader reader) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before calling this method.");
    if(fileName == null)
      throw new IllegalArgumentException("Given file is null");
    if(reader == null)
      throw new IllegalArgumentException("Given reader is null");
    return rubyProvider.getTypePropertiesInfo(fileName, reader);
  }

  /**
   * Returns a list of custom PP types from the given .rb file. The returned
   * list is empty if no type could be found.
   *
   * @param file
   * @return
   * @throws IOException
   *             - if there are errors reading the file
   * @throws IllegalStateException
   *             - if setUp was not called
   */
  public List<PPTypeInfo> getTypeInfo(File file) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getTypeInfo(File).");
    if(file == null)
      throw new IllegalArgumentException("Given file is null - JRubyService.getTypeInfo");
    if(!file.exists())
      throw new FileNotFoundException(file.getPath());
    return rubyProvider.getTypeInfo(file);

  }

  /**
   * Returns a list of custom PP types from the given .rb file. The returned
   * list is empty if no type could be found.
   *
   * @param file
   * @return
   * @throws IOException
   *             - if there are errors reading the file
   * @throws IllegalStateException
   *             - if setUp was not called
   */
  public List<PPTypeInfo> getTypeInfo(String fileName, Reader reader) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getTypeInfo(File).");
    if(fileName == null)
      throw new IllegalArgumentException("Given filename is null - JRubyService.getTypeInfo");
    if(reader == null)
      throw new IllegalArgumentException("Given reader is null - JRubyService.getTypeInfo");
    return rubyProvider.getTypeInfo(fileName, reader);
  }

  /**
   * Returns true if real ruby services are available.
   */
  public boolean isRubyServicesAvailable() {
    return rubyProviderFactory != null;
  }

  /**
   * Loads a puppet distro into a pptp model and saves the result in a file.
   * The distroDir should appoint a directory that contains the directories
   * type and parser/functions. The path to the distroDir should contain a
   * version string segment directly after a segment called 'puppet'. e.g.
   * /somewhere/on/disk/puppet/2.6.2_0/some/path/to/puppet.
   *
   * Will also load default settings:: variables, and meta variables.
   *
   * Output file will contain a PPTP model as a result of this call.
   *
   * @param distroDir
   *            - path to a puppet directory
   * @param outputFile
   * @throws IOException
   * @throws RubySyntaxException
   */
  public void loadAndSaveDistro(File distroDir, File outputFile) throws IOException, RubySyntaxException {
    TargetEntry target = loadDistroTarget(distroDir);

    // Load the default settings:: variables
    loadSettings(target);

    // Load the default meta variables
    loadMetaVariables(target);

    loadPuppetVariables(target);

    // Save the TargetEntry as a loadable resource
    ResourceSet resourceSet = new ResourceSetImpl();
    URI fileURI = URI.createFileURI(outputFile.getAbsolutePath());
    Resource targetResource = resourceSet.createResource(fileURI);
    targetResource.getContents().add(target);
    targetResource.save(null);
  }

  /**
   * Loads a Puppet distribution target. The file should point to the "lib/puppet"
   * directory where the sub-directories "parser" and "type" are. The path to
   * this directory is expected to have a ../../ name on the form puppet-version
   * e.g. /somewhere/puppet-2.6.9/lib/puppet.
   *
   * The implementation will scan the known locations for definitions that
   * should be reflected in the target - i.e. parser/functions/*.rb and
   * type/*.rb
   *
   * Note, this does not load default settings:: variables.
   *
   * @throws IOException
   *             on problems with reading files
   * @throws RubySyntaxException
   *             if there are syntax exceptions in the parsed ruby code
   */
  public TargetEntry loadDistroTarget(File file) throws IOException, RubySyntaxException {
    if(file == null)
      throw new IllegalArgumentException("File can not be null");

    // Create a puppet distro target and parse info from the file path
    //
    PuppetDistribution puppetDistro = PPTPFactory.eINSTANCE.createPuppetDistribution();
    puppetDistro.setDescription("Puppet Distribution");
    IPath path = Path.fromOSString(file.getAbsolutePath());

    // NOTE: This is wrong, will always result in a version == "" as the version is
    // part of the "puppet" directory, not the directory puppet-x.x.x/lib/puppet that is given to
    // this function.
    //
    // String versionString = "";
    // boolean nextIsVersion = false;
    String[] segments = path.segments();
    int sc = segments.length;
    if(segments.length < 3 || !"puppet".equals(segments[sc - 1]) || !"lib".equals(segments[sc - 2]))
      throw new IllegalArgumentException("path to .../puppet/lib is not correct");
    final String distroName = segments[sc - 3];
    if(!distroName.startsWith("puppet-"))
      throw new IllegalArgumentException(
        "The ../../ of the given directory must be named on the form: 'puppet-<version>'");

    puppetDistro.setLabel("puppet");
    // 7 is the first char after 'puppet-'
    puppetDistro.setVersion(distroName.substring(7));

    // Load functions
    File parserDir = new File(file, "parser");
    File functionsDir = new File(parserDir, "functions");
    loadFunctions(puppetDistro, functionsDir);

    // Load logger functions
    for(Function f : loadLoggerFunctions(new File(file, "util/log.rb")))
      puppetDistro.getFunctions().add(f);

    // Load types
    try {
      File typesDir = new File(file, "type");
      loadTypes(puppetDistro, typesDir);

      // load additional properties into types
      // (currently only known such construct is for 'file' type
      // this implementation does however search all subdirectories
      // for such additions
      //
      for(File subDir : typesDir.listFiles(dirFilter))
        loadTypeFragments(puppetDistro, subDir);

    }
    catch(FileNotFoundException e) {
      // ignore
    }

    // load nagios types
    try {
      File nagios = new File(file, "external/nagios/base.rb");
      loadNagiosTypes(puppetDistro, nagios);
    }
    catch(FileNotFoundException e) {
      // ignore - no nagios
    }

    // load metatype
    try {
      File typeFile = new File(file, "type.rb");
      loadMetaType(puppetDistro, typeFile);
    }
    catch(FileNotFoundException e) {
      // ignore
    }

    return puppetDistro;
  }

  /**
   * Load function(s) from a rubyfile (supposed to contain PP function
   * declarations).
   *
   * @param rbFile
   * @param rememberFile
   * @return
   * @throws IOException
   * @throws RubySyntaxException
   */
  public List<Function> loadFunctions(File rbFile) throws IOException, RubySyntaxException {
    return functionInfoToFunction(getFunctionInfo(rbFile));
  }

  /**
   * Load function info into target.
   *
   * @param target
   * @param functionsDir
   * @throws IOException
   * @throws RubySyntaxException
   */
  private void loadFunctions(TargetEntry target, File functionsDir) throws IOException, RubySyntaxException {
    if(functionsDir.isDirectory())
      for(File rbFile : functionsDir.listFiles(rbFileFilter))
        for(Function f : loadFunctions(rbFile))
          target.getFunctions().add(f);
  }

  public List<Function> loadLoggerFunctions(File rbFile) throws IOException, RubySyntaxException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before getTypeInfo(File).");
    if(rbFile == null)
      throw new IllegalArgumentException("Given file is null - JRubyService.getTypeInfo");
    if(!rbFile.exists())
      throw new FileNotFoundException(rbFile.getPath());
    return functionInfoToFunction(rubyProvider.getLogFunctions(rbFile));
  }

  private void loadMetaType(TargetEntry target, File rbFile) throws IOException, RubySyntaxException {
    PPTypeInfo info = getMetaTypeInfo(rbFile);
    MetaType type = PPTPFactory.eINSTANCE.createMetaType();
    type.setName(info.getTypeName());
    type.setDocumentation(info.getDocumentation());
    for(Map.Entry<String, PPTypeInfo.Entry> entry : info.getParameters().entrySet()) {
      Parameter parameter = PPTPFactory.eINSTANCE.createParameter();
      parameter.setName(entry.getKey());
      parameter.setDocumentation(entry.getValue().documentation);
      parameter.setRequired(entry.getValue().isRequired());
      type.getParameters().add(parameter);
    }
    // TODO: Scan the puppet source for providers for the type
    // This is a CHEAT - https://github.com/puppetlabs/geppetto/issues/37
    Parameter p = PPTPFactory.eINSTANCE.createParameter();
    p.setName("provider");
    p.setDocumentation("");
    p.setRequired(false);
    type.getParameters().add(p);

    // TODO: there are more interesting things to pick up (like valid
    // values)
    target.setMetaType(type);

  }

  /**
   * Loads meta variables into the target. These are variables that looks like
   * local variables in every scope, but they are not found from the outside.
   *
   * @param target
   */
  public void loadMetaVariables(TargetEntry target) {
    EList<MetaVariable> metaVars = target.getMetaVariables();

    MetaVariable metaName = PPTPFactory.eINSTANCE.createMetaVariable();
    metaName.setName("name");
    metaName.setDocumentation("");
    metaName.setDeprecated(false);
    metaVars.add(metaName);

    MetaVariable metaTitle = PPTPFactory.eINSTANCE.createMetaVariable();
    metaTitle.setName("title");
    metaTitle.setDocumentation("");
    metaTitle.setDeprecated(false);
    metaVars.add(metaTitle);

    MetaVariable metaModuleName = PPTPFactory.eINSTANCE.createMetaVariable();
    metaModuleName.setName("module_name");
    metaModuleName.setDocumentation("<p>The name of the containing module</p>");
    metaModuleName.setDeprecated(false);
    metaVars.add(metaModuleName);

    MetaVariable callerMetaModuleName = PPTPFactory.eINSTANCE.createMetaVariable();
    callerMetaModuleName.setName("caller_module_name");
    callerMetaModuleName.setDocumentation("<p>The name of the calling module</p>");
    callerMetaModuleName.setDeprecated(false);
    metaVars.add(callerMetaModuleName);
  }

  private void loadNagiosTypes(TargetEntry target, File rbFile) throws IOException, RubySyntaxException {

    for(Type t : transform(getTypeInfo(rbFile))) {
      target.getTypes().add(t);
    }
  }

  public List<TargetEntry> loadPluginsTarget(File pluginsRoot) throws IOException, RubySyntaxException {

    List<TargetEntry> result = Lists.newArrayList();
    // for all the directories in pluginsRoot, load the content of that directory
    // as a module
    if(pluginsRoot == null || !pluginsRoot.isDirectory())
      return result; // do nothing (an empty list)

    for(File pluginRoot : pluginsRoot.listFiles()) {
      String[] nameParts = extractVersionFromName(pluginRoot.getName());
      PuppetDistribution plugin = PPTPFactory.eINSTANCE.createPuppetDistribution();
      plugin.setDescription("Puppet Plugin");
      plugin.setLabel(nameParts[0]);
      plugin.setVersion(nameParts[1]);

      // load functions (lib/puppet/parser/functions/*), and types (lib/puppet/type/*)
      File lib = new File(pluginRoot, "lib/puppet");
      if(!lib.exists())
        continue; // has no content that can be handled

      // Load Functions
      File functionsDir = new File(new File(lib, "parser"), "functions");
      loadFunctions(plugin, functionsDir);

      // Load Types
      try {
        File typesDir = new File(lib, "type");
        loadTypes(plugin, typesDir);

        // load additional properties into types
        // (currently only known such construct is for 'file' type
        // this implementation does however search all subdirectories
        // for such additions
        //
        if(typesDir != null && typesDir.isDirectory())
          for(File subDir : typesDir.listFiles(dirFilter))
            loadTypeFragments(plugin, subDir);

        if(plugin.getFunctions().size() > 0 || plugin.getTypes().size() > 0)
          result.add(plugin);
      }
      catch(FileNotFoundException e) {
        // ignore, just skipping functions, types etc if not present in plugin
      }
    }
    return result;
  }

  public void loadPuppetVariables(TargetEntry target) {
    TPVariable var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("environment");
    var.setDocumentation("The node's current environment. Available when compiling a catalog for a node.");
    var.setDeprecated(false);
    target.getContents().add(var);

    var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("clientcert");
    var.setDocumentation("The node's certname setting. Available when compiling a catalog for a node.");
    var.setDeprecated(false);
    target.getContents().add(var);

    var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("clientversion");
    var.setDocumentation("The current version of the puppet agent. Available when compiling a catalog for a node.");
    var.setDeprecated(false);
    target.getContents().add(var);

    var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("servername");
    var.setDocumentation(Joiner.on("").join(
      ImmutableList.of(
        "The puppet master’s fully-qualified domain name. (Note that this information ",
        "is gathered from the puppet master by Facter, rather than read from the config files; even if the ",
        "master’s certname is set to something other than its fully-qualified domain name, this variable ",
        "will still contain the server’s fqdn.)")));
    var.setDeprecated(false);
    target.getContents().add(var);

    var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("serverip");
    var.setDocumentation("The puppet master's IP address");
    var.setDeprecated(false);
    target.getContents().add(var);

    var = PPTPFactory.eINSTANCE.createTPVariable();
    var.setName("serverversion");
    var.setDocumentation("The current version of puppet on the puppet master.");
    var.setDeprecated(false);
    target.getContents().add(var);

  }

  /**
   * Loads predefined variables in the settings:: namespace. These are hard to
   * find in the puppet logic.
   *
   * @param target
   */
  public void loadSettings(TargetEntry target) {
    NameSpace settings = PPTPFactory.eINSTANCE.createNameSpace();
    settings.setReserved(true);
    settings.setName("settings");
    target.getContents().add(settings);

    // Create a wildcard to match all settings::*
    //
    TPVariable wildcard = PPTPFactory.eINSTANCE.createTPVariable();
    wildcard.setName("*");
    wildcard.setAssignable(false);
    settings.getContents().add(wildcard);

    // Add known names in settings (the most common ones). This to avoid
    // warnings
    //
    SettingsData settingsData = new SettingsData();
    for(SettingsData.Setting s : settingsData.settings) {
      addTPVariable(settings, s.name, new RubyDocProcessor().asHTML(s.documentation), s.deprecated);
    }
  }

  public List<TypeFragment> loadTypeFragments(File rbFile) throws IOException, RubySyntaxException {
    List<TypeFragment> result = Lists.newArrayList();
    for(PPTypeInfo type : getTypeFragments(rbFile)) {
      TypeFragment fragment = PPTPFactory.eINSTANCE.createTypeFragment();
      fragment.setName(type.getTypeName());

      // add the properties (will typically load just one).
      for(Map.Entry<String, PPTypeInfo.Entry> entry : type.getProperties().entrySet()) {
        Property property = PPTPFactory.eINSTANCE.createProperty();
        property.setName(entry.getKey());
        property.setDocumentation(entry.getValue().documentation);
        property.setRequired(entry.getValue().isRequired());
        fragment.getProperties().add(property);
      }

      // add the parameters (will typically load just one).
      for(Map.Entry<String, PPTypeInfo.Entry> entry : type.getParameters().entrySet()) {
        Parameter parameter = PPTPFactory.eINSTANCE.createParameter();
        parameter.setName(entry.getKey());
        parameter.setDocumentation(entry.getValue().documentation);
        parameter.setRequired(entry.getValue().isRequired());
        fragment.getParameters().add(parameter);
      }

      result.add(fragment);
    }
    return result;

  }

  private void loadTypeFragments(TargetEntry target, File subDir) throws IOException, RubySyntaxException {
    for(File f : subDir.listFiles(rbFileFilter)) {
      // try to get type property additions
      List<TypeFragment> result = loadTypeFragments(f);
      for(TypeFragment tf : result)
        target.getTypeFragments().add(tf);
    }
  }

  /**
   * Load type(s) from ruby file.
   *
   * @param rbFile
   *            - the file to parse
   * @param rememberFiles
   *            - if entries should remember the file they came from.
   * @return
   * @throws IOException
   * @throws RubySyntaxException
   */
  public List<Type> loadTypes(File rbFile) throws IOException, RubySyntaxException {
    // List<Type> result = Lists.newArrayList();
    return transform(getTypeInfo(rbFile));

    // for (PPTypeInfo info : getTypeInfo(rbFile)) {
    // Type type = PPTPFactory.eINSTANCE.createType();
    // type.setName(info.getTypeName());
    // type.setDocumentation(info.getDocumentation());
    // for (Map.Entry<String, PPTypeInfo.Entry> entry : info
    // .getParameters().entrySet()) {
    // Parameter parameter = PPTPFactory.eINSTANCE.createParameter();
    // parameter.setName(entry.getKey());
    // parameter.setDocumentation(entry.getValue().documentation);
    // parameter.setRequired(entry.getValue().isRequired());
    // type.getParameters().add(parameter);
    // }
    // for (Map.Entry<String, PPTypeInfo.Entry> entry : info
    // .getProperties().entrySet()) {
    // Property property = PPTPFactory.eINSTANCE.createProperty();
    // property.setName(entry.getKey());
    // property.setDocumentation(entry.getValue().documentation);
    // property.setRequired(entry.getValue().isRequired());
    // type.getProperties().add(property);
    // }
    // result.add(type);
    // }
    // return result;
  }

  /**
   * Load type info into target.
   *
   * @param target
   * @param typesDir
   * @throws IOException
   * @throws RubySyntaxException
   */
  private void loadTypes(TargetEntry target, File typesDir) throws IOException, RubySyntaxException {
    if(!typesDir.isDirectory())
      return;
    for(File rbFile : typesDir.listFiles(rbFileFilter))
      for(Type t : loadTypes(rbFile))
        target.getTypes().add(t);
  }

  /**
   * Parse a .rb file and return information about syntax errors and warnings.
   * Must be preceded with a call to setUp().
   *
   * @param file
   * @return
   * @throws IOException
   * @throws IllegalStateException
   *             if setUp was not called.
   */
  public IRubyParseResult parse(File file) throws IOException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before parse(File).");
    return rubyProvider.parse(file);
  }

  /**
   * Parse a .rb file and return information about syntax errors and warnings.
   * Must be preceded with a call to setUp().
   *
   * @param file
   * @return
   * @throws IOException
   * @throws IllegalStateException
   *             if setUp was not called.
   */
  public IRubyParseResult parse(String path, Reader reader) throws IOException {
    if(rubyProvider == null)
      throw new IllegalStateException("Must call setUp() before parse(File).");
    return rubyProvider.parse(path, reader);
  }

  /**
   * Should be called to initiate the ruby services. Each call to setUp should
   * be paired with a call to tearDown or resources will be wasted.
   */
  public synchronized void setUp() {
    if(rubyProvider == null)
      rubyProvider = rubyProviderFactory == null
          ? new MockRubyServices()
          : rubyProviderFactory.create();
    rubyProvider.setUp();
  }

  public void tearDown() {
    if(rubyProvider == null)
      return; // ignore silently

    rubyProvider.tearDown();
  }

  private List<Type> transform(List<PPTypeInfo> typeInfos) {
    List<Type> result = Lists.newArrayList();
    for(PPTypeInfo info : typeInfos) {
      Type type = PPTPFactory.eINSTANCE.createType();
      type.setName(info.getTypeName());
      type.setDocumentation(info.getDocumentation());
      for(Map.Entry<String, PPTypeInfo.Entry> entry : info.getParameters().entrySet()) {
        Parameter parameter = PPTPFactory.eINSTANCE.createParameter();
        parameter.setName(entry.getKey());
        parameter.setDocumentation(entry.getValue().documentation);
        parameter.setRequired(entry.getValue().isRequired());
        parameter.setNamevar(entry.getValue().isNamevar());
        type.getParameters().add(parameter);
      }
      for(Map.Entry<String, PPTypeInfo.Entry> entry : info.getProperties().entrySet()) {
        Property property = PPTPFactory.eINSTANCE.createProperty();
        property.setName(entry.getKey());
        property.setDocumentation(entry.getValue().documentation);
        property.setRequired(entry.getValue().isRequired());
        type.getProperties().add(property);
      }
      result.add(type);
    }
    return result;

  }
}
TOP

Related Classes of com.puppetlabs.geppetto.ruby.RubyHelper

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.