/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.flex.compiler.internal.parsing; import java.io.StringReader; public class SourceFragmentsReader extends StringReader { /** * Concatenates the logical text of multiple source fragments. */ public static String concatLogicalText(ISourceFragment[] sourceFragments) { StringBuilder sb = new StringBuilder(); for (ISourceFragment sourceFragment : sourceFragments) { sb.append(sourceFragment.getLogicalText()); } return sb.toString(); } /** * Constructor. */ SourceFragmentsReader(ISourceFragment[] sourceFragments) { // This class extends StringReader. // We want the StringReader to read the concatenated logical text // of all the fragments. super(concatLogicalText(sourceFragments)); this.sourceFragments = sourceFragments; // Build an array which keeps tracks of the fragment boundaries // as logical offsets. int n = sourceFragments.length; logicalFragmentBoundaries = new int[n + 1]; logicalFragmentBoundaries[0] = 0; for (int i = 0; i < n; i++) { logicalFragmentBoundaries[i + 1] = logicalFragmentBoundaries[i] + sourceFragments[i].getLogicalText().length(); } } public SourceFragmentsReader(String sourcePath, ISourceFragment[] sourceFragments) { this(sourceFragments); this.sourcePath = sourcePath; } private String sourcePath; /** * The source fragments that we're reading. */ private ISourceFragment[] sourceFragments; /** * The boundaries between source fragments, as logical offsets. * For example, if there are two fragments, one with logical length 3 * and one with logical length 5, then this array will be [0, 3, 8]. * This information allows us to take a starting or ending * logical offset and determine which fragment it came from. */ private int[] logicalFragmentBoundaries; public void adjustLocation(TokenBase token) { token.setSourcePath(sourcePath); // Note that fragments are not necessarily physically contiguous, // and a particular logical offset could be the end of fragment n // and the beginning of fragment n + 1. Therefore, we need different // logic for mapping logical-start-to-physical-start and // logical-end-to-physical-end. For example, suppose there are // two fragments: // // physical-start physical-end logical-start logical-end // #0 100 105 0 1 // #1 110 115 1 6 // // Then a logical start of 1 maps to a physical start of 110, // while a logical end of 1 maps to a physical end of 105. // Convert token's 'start' from logical to physical. int logicalStart = token.getStart(); int i = getFragmentIndexForStart(logicalStart); int startDelta = logicalStart - logicalFragmentBoundaries[i]; int physicalStart = sourceFragments[i].getPhysicalStart(); physicalStart += startDelta; token.setStart(physicalStart); // Convert token's 'end' from logical to physical. int logicalEnd = token.getEnd(); int j = getFragmentIndexForEnd(logicalEnd); int endDelta = logicalFragmentBoundaries[j + 1] - logicalEnd; int physicalEnd = sourceFragments[j].getPhysicalStart() + sourceFragments[j].getPhysicalText().length(); physicalEnd -= endDelta; token.setEnd(physicalEnd); // Convert token's 'line' from logical to physical. int logicalLine = token.getLine(); int physicalLine = logicalLine + sourceFragments[i].getPhysicalLine();; token.setLine(physicalLine); // Convert token's 'column' from logical to physical. int logicalColumn = token.getColumn(); int physicalColumn = logicalColumn + sourceFragments[i].getPhysicalColumn(); token.setColumn(physicalColumn); } private int getFragmentIndexForStart(int start) { int n = logicalFragmentBoundaries.length; for (int i = n - 1; i >= 0; i--) { if (logicalFragmentBoundaries[i] <= start) return i; } return -1; } private int getFragmentIndexForEnd(int end) { int n = logicalFragmentBoundaries.length; for (int i = 0; i < n; i++) { if (logicalFragmentBoundaries[i] >= end) return i - 1; } return -1; } public int getLogicalEnd() { int n = logicalFragmentBoundaries.length; return logicalFragmentBoundaries[n - 1]; } @Override public String toString() { return concatLogicalText(sourceFragments); } }