Package org.rstudio.studio.client.rmarkdown.model

Source Code of org.rstudio.studio.client.rmarkdown.model.YamlTree$YamlTreeNode

/*
* YamlTree.java
*
* Copyright (C) 2009-14 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.studio.client.rmarkdown.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.rstudio.core.client.Debug;

import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;

public class YamlTree
{
   private class YamlTreeNode
   {
      public YamlTreeNode(String line)
      {
         // the YAML package outputs tildes to represent null values; these
         // are valid YAML but "null" is easier to read for R users
         if (line.endsWith(": ~"))
         {
            line = line.substring(0, line.length() - 1) + "null";
         }
         yamlLine = line;
         key = getKey(line);
         indentLevel = getIndentLevel();
      }
     
      // add a child node; returns true iff the node was added (we may choose
      // to treat the child as a continuation)
      public boolean addChild(YamlTreeNode child)
      {
         // if this node has a key, add it as a tree node; otherwise, add its
         // line data to this node (as a multi-line continuation)
         if (child.key.length() > 0)
         {
            children.add(child);
            child.parent = this;
            return true;
         }

         yamlLine += ("\n" + child.yamlLine);
         return false;
      }
     
      public String getIndent()
      {
         // consider the list element indicator (-) to be part of the node's
         // indentation, to prevent list continuations from being treated as
         // sibling nodes
         RegExp whitespace = RegExp.compile("^\\s*-?\\s*");
         MatchResult result = whitespace.exec(yamlLine);
         if (result == null)
            return "";
         else
            return result.getGroup(0);
      }
     
      public String getValue()
      {
         int idx = yamlLine.indexOf(":");
         if (idx < 0)
            return "";
         return yamlLine.substring(idx + 2).trim();
      }
     
      public void setValue(String value)
      {
         int idx = yamlLine.indexOf(":");
         if (idx < 0)
            return;
         yamlLine = yamlLine.substring(0, idx + 2) + value;
      }
     
      public String yamlLine;
      public String key;
      public int indentLevel = 0;
      public YamlTreeNode parent = null;
      public List<YamlTreeNode> children = new ArrayList<YamlTreeNode>();
     
      private String getKey(String line)
      {
         RegExp keyReg = RegExp.compile("^\\s*([^:]+):");
         MatchResult result = keyReg.exec(line);
         if (result == null)
            return "";
          else
            return result.getGroup(1);
      }

      private int getIndentLevel()
      {
         return getIndent().length();
      }
   }
  
   public YamlTree(String yaml)
   {
      root_ = createYamlTree(yaml);
      keyMap_ = new HashMap<String, YamlTreeNode>();
      createKeyMap(root_, keyMap_);
   }
  
   public String toString()
   {
      return yamlFromTree(root_);
   }
  
   public void reorder(List<String> orderedKeys)
   {
      // Sort the YAML lines into a tree
      if (!keyMap_.containsKey(orderedKeys.get(0)))
         return;
      YamlTreeNode parent = keyMap_.get(orderedKeys.get(0)).parent;
     
      // Move around subtrees to match the given list of ordered keys
      // (swap subtrees into a position matching that specified)
      for (int i = 0; i < orderedKeys.size(); i++)
      {
         for (int j = i; j < parent.children.size(); j++)
         {
            YamlTreeNode child = parent.children.get(j);
            if (orderedKeys.get(i) == child.key)
            {
               YamlTreeNode previousChild = parent.children.get(i);
               parent.children.set(i, child);
               parent.children.set(j, previousChild);
            }
         }
      }
   }

   public List<String> getChildKeys(String parentKey)
   {
      try
      {
         if (!keyMap_.containsKey(parentKey))
            return null;
         YamlTreeNode parent = keyMap_.get(parentKey);
         ArrayList<String> result = new ArrayList<String>();
         for (YamlTreeNode child: parent.children)
         {
            result.add(child.key);
         }
         return result;
      }
      catch (Exception e)
      {
         // case 3826: it's not clear why but we've seen an exception thrown
         // from the above in non-reproducible circumstances; handle gracefully
         Debug.log("YamlTree: Couldn't get child keys for " + parentKey +
                   " (" + e.getMessage() + ")");
      }
      return null;
   }
  
   // add a simple YAML value to the tree under the given parent, or under
   // the root if parentKey is null.
   public void addYamlValue(String parentKey, String key, String value)
   {
      // if a key was specified but doesn't exist in the flattened map, abort
      if (parentKey != null && !keyMap_.containsKey(parentKey))
         return;
     
      String line = key + ": " + value;    
      YamlTreeNode parent = parentKey == null ? root_ : keyMap_.get(parentKey);
      YamlTreeNode child = parentKey == null ? new YamlTreeNode(line) :
            new YamlTreeNode(parent.getIndent() + "  " + line);
      keyMap_.put(key, child);
      parent.addChild(child);
   }
  
   public String getKeyValue(String key)
   {
      if (keyMap_.containsKey(key))
         return keyMap_.get(key).getValue();
      return "";
   }

   public void setKeyValue(String key, String value)
   {
      if (keyMap_.containsKey(key))
         keyMap_.get(key).setValue(value);
   }

   private YamlTreeNode createYamlTree(String yaml)
   {
      String[] lines = yaml.split("\n");
      YamlTreeNode root = new YamlTreeNode("");
      root.indentLevel = -2;
      YamlTreeNode currentParent = root;
      YamlTreeNode lastNode = root;
      int currentIndentLevel = 0;
      for (String line: lines)
      {
         YamlTreeNode child = new YamlTreeNode(line);
         if (child.indentLevel > currentIndentLevel)
         {
            // Descending: we're recording children of the previous line
            currentParent = lastNode;
         }
         else if (child.indentLevel < currentIndentLevel)
         {
            // Ascending: find the first parent node in the hierarchy that has
            // an indent level less than this node's
            do
            {
               currentParent = currentParent.parent;
            }
            while (currentParent != null &&
                   currentParent.indentLevel >= child.indentLevel);
           
            // if we unwound all the way to the top, use the root as the parent
            if (currentParent == null)
               currentParent = root;
         }

         currentIndentLevel = child.indentLevel;
         if (currentParent.addChild(child))
            lastNode = child;
      }
      return root;
   }
  
   private static String yamlFromTree(YamlTreeNode root)
   {
      String yaml = "";
      for (YamlTreeNode child: root.children)
      {
         yaml += (child.yamlLine + '\n');
         yaml += yamlFromTree(child);
      }
      return yaml;
   }
  
   private static void createKeyMap(YamlTreeNode root,
                                    Map<String, YamlTreeNode> output)
   {
      for (YamlTreeNode child: root.children)
      {
         String key = child.key;
         // add this key if it doesn't already exist, or if it does exist and
         // this key is closer to the root than the existing key
         if (key.length() > 0 &&
             (!output.containsKey(key) ||
              child.indentLevel < output.get(key).indentLevel))
         {
            output.put(child.key, child);
         }
         createKeyMap(child, output);
      }
   }
  
   // A flattened version of the tree as a list of key-value pairs. In the
   // case where multiple keys exist, the key closest to the root is stored.
   private final Map<String, YamlTreeNode> keyMap_;
   private final YamlTreeNode root_;
}
TOP

Related Classes of org.rstudio.studio.client.rmarkdown.model.YamlTree$YamlTreeNode

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.