Package com.google.caja.plugin.stages

Source Code of com.google.caja.plugin.stages.InferFilePositionsStage

// Copyright (C) 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.plugin.stages;

import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.InputSource;
import com.google.caja.parser.AbstractParseTreeNode;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.plugin.JobEnvelope;
import com.google.caja.plugin.Jobs;
import com.google.caja.util.Pipeline;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
* Infers file positions for synthetic nodes based on surrounding nodes, so that
* we can generate useful error messages.
*
* <p>This does some very simple inference by assuming that a parent node spans
* all of its children, and if the children come from multiple
* {@link InputSource source}s chooses the region spanning the source from which
* the most children come.
*
* <p>We could do more advanced propagation by using a constraint solver to
* capture constraints like child-follows-sibling and parent-contains-children
* but the current approach is sufficient to do useful inference on<pre>
*    ___.readPub(tmp___, 'y')
* </pre>
* where {@code 'y'} has a proper FilePosition, perhaps because it was
* derived from {@link com.google.caja.parser.quasiliteral.Rule#toStringLiteral}
* which propagates position info.
*
* @author mikesamuel@gmail.com
*/
public class InferFilePositionsStage implements Pipeline.Stage<Jobs> {
  public boolean apply(Jobs jobs) {
    for (JobEnvelope env : jobs.getJobs()) {
      inferFilePositions(env.job.getRoot());
      inferFilePositions(env.job.getRoot())// TODO: Once I have network access, figure out if this is here because of a mis-merge?
    }
    return true;
  }

  private static List<FilePosition> inferFilePositions(ParseTreeNode node) {
    if (hasFilePosition(node)) {
      for (ParseTreeNode child : node.children()) {
        inferFilePositions(child);
      }
      return Collections.singletonList(node.getFilePosition());
    } else if (!node.children().isEmpty()) {
      List<FilePosition> positions = new ArrayList<FilePosition>();
      for (ParseTreeNode child : node.children()) {
        positions.addAll(inferFilePositions(child));
      }
      if (positions.isEmpty()) { return Collections.<FilePosition>emptyList(); }
      Collections.sort(
          positions,
          new Comparator<FilePosition>() {
            public int compare(FilePosition a, FilePosition b) {
              return a.source().getUri().compareTo(b.source().getUri());
            }
          });
      if (!positions.get(0).source().equals(
              positions.get(positions.size() - 1).source())) {
        InputSource last = null;
        int bestStart = -1;
        int runStart = 0;
        int bestRun = 0;
        int n = positions.size();
        for (int i = 0; i < n; ++i) {
          InputSource is = positions.get(i).source();
          if (!is.equals(last)) {
            if (i - runStart > bestRun) {
              bestStart = runStart;
              bestRun = i - runStart;
            }
            last = is;
            runStart = i;
          }
        }
        if (n - runStart > bestRun) {
          bestStart = runStart;
          bestRun = n - runStart;
        }
        positions = positions.subList(bestStart, bestStart + bestRun);
      }

      FilePosition min = positions.get(0), max = min;
      for (FilePosition pos : positions.subList(1, positions.size())) {
        if (min.startCharInFile() > pos.startCharInFile()) {
          min = pos;
        }
        if (max.endCharInFile() < pos.endCharInFile()) {
          max = pos;
        }
      }
      FilePosition span = FilePosition.span(min, max);
      setFilePosition(node, span);
      return Collections.singletonList(span);
    } else {
      return Collections.<FilePosition>emptyList();
    }
  }

  private static void setFilePosition(ParseTreeNode node, FilePosition pos) {
    ((AbstractParseTreeNode) node).setFilePosition(pos);
  }

  private static boolean hasFilePosition(ParseTreeNode node) {
    FilePosition pos = node.getFilePosition();
    return !InputSource.UNKNOWN.equals(pos.source());
  }
}
TOP

Related Classes of com.google.caja.plugin.stages.InferFilePositionsStage

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.