Package org.rascalmpl.library.lang.yaml

Source Code of org.rascalmpl.library.lang.yaml.RascalYAML

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* 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:
*
*   * Tijs van der Storm - storm@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.library.lang.yaml;

import static org.rascalmpl.library.lang.yaml.YAMLTypeFactory.Node;
import static org.rascalmpl.library.lang.yaml.YAMLTypeFactory.Node_mapping;
import static org.rascalmpl.library.lang.yaml.YAMLTypeFactory.Node_reference;
import static org.rascalmpl.library.lang.yaml.YAMLTypeFactory.Node_scalar;
import static org.rascalmpl.library.lang.yaml.YAMLTypeFactory.Node_sequence;

import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.imp.pdb.facts.IBool;
import org.eclipse.imp.pdb.facts.IConstructor;
import org.eclipse.imp.pdb.facts.IDateTime;
import org.eclipse.imp.pdb.facts.IInteger;
import org.eclipse.imp.pdb.facts.IList;
import org.eclipse.imp.pdb.facts.IListWriter;
import org.eclipse.imp.pdb.facts.IMap;
import org.eclipse.imp.pdb.facts.IMapWriter;
import org.eclipse.imp.pdb.facts.IReal;
import org.eclipse.imp.pdb.facts.ISourceLocation;
import org.eclipse.imp.pdb.facts.IString;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.eclipse.imp.pdb.facts.type.TypeFactory;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.TypeReifier;
import org.rascalmpl.interpreter.asserts.ImplementationError;
import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory;
import org.yaml.snakeyaml.Yaml;

public class RascalYAML {
  private static final String ANCHOR_ANNO = "anchor";
  private final IValueFactory values;
  private final TypeReifier reifier;
 
  private static final TypeFactory tf = TypeFactory.getInstance();
 
  public RascalYAML(IValueFactory values) {
    super();
    this.values = values;
    this.reifier = new TypeReifier(values);
  }
 
  public IConstructor loadYAML(IString src, IEvaluatorContext ctx) {
    Yaml yaml = new Yaml();
    Object obj = yaml.load(src.getValue());
    if (obj == null) {
      throw RuntimeExceptionFactory.illegalArgument(src, ctx.getCurrentAST(), ctx.getStackTrace());
    }
    IdentityHashMap<Object, Integer> anchors = new IdentityHashMap<Object, Integer>();
    computeAnchors(obj, anchors, 0);
    return loadRec(obj, anchors, new IdentityHashMap<Object, Boolean>(), ctx);
  }
 
  @SuppressWarnings("unchecked")
  private int computeAnchors(Object obj, IdentityHashMap<Object, Integer> anchors, int a) {
    if (anchors.containsKey(obj)) {
      anchors.put(obj, a);
      return a + 1;
    }
    anchors.put(obj, -1);
   
    if (obj instanceof Object[]) {
      for (Object elt: (Object[])obj) {
        a = computeAnchors(elt, anchors, a);
      }
    }
    else if (obj instanceof List) {
      for (Object elt: (List<Object>)obj) {
        a = computeAnchors(elt, anchors, a);
      }
    }
    else if (obj instanceof Map) {
      Map<Object, Object> m = (Map<Object,Object>)obj;
      for (Map.Entry<Object,Object> e: m.entrySet()) {
        a = computeAnchors(e.getKey(), anchors, a);
        a = computeAnchors(e.getValue(), anchors, a);
      }
    }
    return a;
  }
 
  @SuppressWarnings("unchecked")
  private IConstructor loadRec(Object obj, IdentityHashMap<Object, Integer> anchors, IdentityHashMap<Object, Boolean> visited, IEvaluatorContext ctx) {
    if (obj instanceof Integer) {
      return values.constructor(Node_scalar, values.integer((Integer)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.integerType(), ctx).getValue());
    }
    if (obj instanceof Long) {
      return  values.constructor(Node_scalar, values.integer((Long)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.integerType(), ctx).getValue());
    }
    if (obj instanceof Double) {
      return values.constructor(Node_scalar, values.real((Double)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.realType(), ctx).getValue());
    }
    if (obj instanceof Float) {
      return values.constructor(Node_scalar, values.real((Float)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.realType(), ctx).getValue());
    }
    if (obj instanceof String) {
      return values.constructor(Node_scalar, values.string((String)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.stringType(), ctx).getValue());
    }
    if (obj instanceof Boolean) {
      return values.constructor(Node_scalar, values.bool((Boolean)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.boolType(), ctx).getValue());
    }
    if (obj instanceof Date) {
      return values.constructor(Node_scalar, values.datetime(((Date)obj).getTime()))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.dateTimeType(), ctx).getValue());
    }
    if (obj instanceof URI) {
      return values.constructor(Node_scalar, values.sourceLocation((URI)obj))
          .asAnnotatable().setAnnotation("tag", reifier.typeToValue(tf.sourceLocationType(), ctx).getValue());
    }

    // Structural types may be shared.
    if (visited.containsKey(obj)) {
      assert anchors.get(obj) != - 1;
      return values.constructor(Node_reference, values.integer(anchors.get(obj)));
    }
    visited.put(obj, true);
   
    IConstructor result;
    if (obj instanceof Object[]) {
      IListWriter w = values.listWriter(Node);
      for (Object elt: (Object[])obj) {
        w.append(loadRec(elt, anchors, visited, ctx));
      }
      result = values.constructor(Node_sequence, w.done());
    }
    else if (obj instanceof List) {
      IListWriter w = values.listWriter(Node);
      for (Object elt: (List<Object>)obj) {
        w.append(loadRec(elt, anchors, visited, ctx));
      }
      result = values.constructor(Node_sequence, w.done());
    }
    else if (obj instanceof Map) {
      IMapWriter w = values.mapWriter(Node, Node);
      Map<Object, Object> m = (Map<Object,Object>)obj;
      for (Map.Entry<Object,Object> e: m.entrySet()) {
        w.put(loadRec(e.getKey(), anchors, visited, ctx), loadRec(e.getValue(), anchors, visited, ctx));
      }
      result = values.constructor(Node_mapping, w.done());
    }
    else {
      throw RuntimeExceptionFactory.illegalArgument(
          values.string(obj.toString() + " (class=" + obj.getClass() + ")"),
          ctx.getCurrentAST(), ctx.getStackTrace());
    }
    if (anchors.get(obj) != -1) {
      result = result.asAnnotatable().setAnnotation(ANCHOR_ANNO, values.integer(anchors.get(obj)));
    }
    return result;
  }
 
  public IString dumpYAML(IConstructor yaml, IEvaluatorContext ctx) {
    Map<Integer, Object> visited = new HashMap<Integer, Object>();
    Object obj = dumpYAMLrec(yaml, visited, ctx);
    Yaml y = new Yaml();
    return values.string(y.dump(obj));
  }

  private Object dumpYAMLrec(IConstructor yaml, Map<Integer, Object> visited, IEvaluatorContext ctx) {
    // in valid YAML anchors always occur before any references
    // this should also hold in our YAML data type
    if (yaml.getConstructorType() == Node_reference) {
      int id = ((IInteger)yaml.get(0)).intValue();
      if (!visited.containsKey(id)) {
        throw RuntimeExceptionFactory.indexOutOfBounds(values.integer(id), ctx.getCurrentAST(), ctx.getStackTrace());
      }
      return visited.get(id);
    }
    if (yaml.getConstructorType() == Node_scalar) {
      // we're ignoring the tag annotations right now.
      IValue value = yaml.get(0);
      if (value.getType() == tf.integerType()) {
        // TODO: detect Long, BigInt etc.
        return new Integer(((IInteger)value).intValue());
      }
      if (value.getType() == tf.realType()) {
        return new Double(((IReal)value).doubleValue());
      }
      if (value.getType() == tf.stringType()) {
        return new String((((IString)value).getValue()));
      }
      if (value.getType() == tf.boolType()) {
        return new Boolean((((IBool)value).getValue()));
      }
      if (value.getType() == tf.dateTimeType()) {
        return new Date(((IDateTime)value).getInstant());
      }
      if (value.getType() == tf.sourceLocationType()) {
        return ((ISourceLocation)value).getURI();
      }
      throw RuntimeExceptionFactory.illegalArgument(yaml, ctx.getCurrentAST(), ctx.getStackTrace());
    }
    if (yaml.getConstructorType() == Node_sequence) {
      List<Object> l = new ArrayList<Object>();
      if (yaml.asAnnotatable().hasAnnotation(ANCHOR_ANNO)) {
        visited.put(((IInteger)yaml.asAnnotatable().getAnnotation(ANCHOR_ANNO)).intValue(), l);
      }
      for (IValue v: (IList)yaml.get(0)) {
        l.add(dumpYAMLrec((IConstructor)v, visited, ctx));
      }
      return l;
    }
    if (yaml.getConstructorType() == Node_mapping) {
      Map<Object, Object> m = new IdentityHashMap<Object, Object>();
      if (yaml.asAnnotatable().hasAnnotation(ANCHOR_ANNO)) {
        visited.put(((IInteger)yaml.asAnnotatable().getAnnotation(ANCHOR_ANNO)).intValue(), m);
      }
      Iterator<Entry<IValue, IValue>> iter = ((IMap)yaml.get(0)).entryIterator();
      while (iter.hasNext()) {
        Entry<IValue, IValue> e = iter.next();
        m.put(dumpYAMLrec((IConstructor)e.getKey(), visited, ctx),
            dumpYAMLrec((IConstructor)e.getValue(), visited, ctx));
      }
      return m;
    }
    throw new ImplementationError("Invalid YAML data type: " + yaml);
  }
 
}
TOP

Related Classes of org.rascalmpl.library.lang.yaml.RascalYAML

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.