All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.css.SourceCodeLocation Maven / Gradle / Ivy

Go to download

Closure Stylesheets is an extension to CSS that adds variables, functions, conditionals, and mixins to standard CSS. The tool also supports minification, linting, RTL flipping, and CSS class renaming.

There is a newer version: 1.8.0
Show newest version
/*
 * Copyright 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.common.css;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;

import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Iterator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * A location in source code. A location is a sequence of adjacent characters
 * that usually represent a token or a larger language construct.
 *
 * 

Error messages represent the most common use of this class, as an error * message usually relates to a source code location. The location related to * some messages might not be known; this class has a special value that * represents an "unknown" location. * *

Character sequences this class points to can have 0 length. If that is the * case, the actual location is that "point" between two source code characters. * This usually means that something happens from that point onwards, or that an * error has been detected at that point but there is no information regarding * the actual token that caused the error. * *

Instances of this class are immutable. */ public class SourceCodeLocation implements Comparable, Serializable { /** * This describes a point in a string. A point is the location between two * characters and is indicated by the character index of the immediately * following character. For example, in the string "abc", point 0 refers to * the location immediately before the 'a' and point 2 to the location before * the 'c'. * *

For convenience we also store the line number of the point (the line * that contains the character following the point) and the index in that * line. Both of these indices start at 1. The first line of a file is line 1 * and the point before its first character has index 1 on line 1. * *

The exact definition of lines depends on the language conventions and is * best left for the parser to handle. If you want to display the text at that * location, either use the character index or use the line number and the * index in the line together with lines as they were split by the parser. * *

It might happen that the source code point for something is not known. * This is modeled by a point with the special value -1 for the character * index. The line and the index on the line must be 0 in this case. * *

Instances of this class are immutable. */ public static class SourceCodePoint implements Comparable, Serializable { /** * The index of the character immediately after the source code point. * Indices start at 0; -1 means the location is not known. */ private final int characterIndex; /** * The number of the line that contains the character at characterIndex. * Numbers start at 1; 0 means the location is not known. */ private final int lineNumber; /** * The index in the line identified by lineNumber of the character at * characterIndex. Numbers start at 1; 0 means the location is not known. */ private final int indexInLine; SourceCodePoint(int characterIndex, int lineNumber, int indexInLine) { this.lineNumber = lineNumber; this.indexInLine = indexInLine; this.characterIndex = characterIndex; if (!hasValidKnownCoordinates() && !hasValidUnknownCoordinates()) { throw new IllegalArgumentException( String.format( "The location passed " + "(lineNumber %d, indexInLine %d, characterIndex %d) " + "is not valid.", lineNumber, indexInLine, characterIndex)); } if (!hasPlausibleCoordinates()) { throw new IllegalArgumentException( String.format( "The location passed " + "(lineNumber %d, indexInLine %d, characterIndex %d) " + "is not plausible.", lineNumber, indexInLine, characterIndex)); } } SourceCodePoint(SourceCodePoint that) { this(that.characterIndex, that.lineNumber, that.indexInLine); } boolean hasValidKnownCoordinates() { return lineNumber >= 1 && indexInLine >= 1 && characterIndex >= 0; } boolean hasValidUnknownCoordinates() { return characterIndex == -1 && lineNumber == 0 && indexInLine == 0; } boolean hasPlausibleCoordinates() { return characterIndex >= lineNumber - 1 + indexInLine - 1; } int getCharacterIndex() { return characterIndex; } int getLineNumber() { return lineNumber; } int getIndexInLine() { return indexInLine; } boolean isUnknown() { return characterIndex == -1; } @Override public boolean equals(@Nullable Object o) { if (o == null) { return false; } if (!(o instanceof SourceCodePoint)) { return false; } SourceCodePoint other = (SourceCodePoint) o; boolean areEqual = this.characterIndex == other.characterIndex; if (areEqual) { Preconditions.checkState((this.lineNumber == other.lineNumber) && (this.indexInLine == other.indexInLine), "Character indexes are equal but line numbers and indexes within " + "the line do not match."); } else { Preconditions.checkState((this.lineNumber != other.lineNumber) || (this.indexInLine != other.indexInLine), "Line numbers and indexes within the line match but character " + "indexes are not equal"); } return areEqual; } @Override public int hashCode() { return characterIndex; } @Override public int compareTo(SourceCodePoint o) { Preconditions.checkNotNull(o); return Ints.compare(this.characterIndex, o.characterIndex); } } private static final SourceCode UNKNOWN_SOURCE_CODE = new SourceCode("unknown", ""); private static final Function LOCATABLE_TO_LOCATION = Locatable::getSourceCodeLocation; /** * Returns an unknown location. */ public static SourceCodeLocation getUnknownLocation() { SourceCodeLocation result = new SourceCodeLocation( UNKNOWN_SOURCE_CODE, -1 /* beginCharacterIndex */, 0 /* beginLineNumber */, 0 /* beginIndexInLine */, -1 /* endCharacterindex */, 0 /* endLineNumber */, 0 /* endIndexInLine */); Preconditions.checkState(result.isUnknown()); Preconditions.checkState(result.begin.hasValidUnknownCoordinates()); Preconditions.checkState(result.end.hasValidUnknownCoordinates()); return result; } /** * Returns a new SourceCodeLocation which covers everything between the beginning of the first * location and the end of the second location. */ public static SourceCodeLocation merge( SourceCodeLocation beginLocation, SourceCodeLocation endLocation) { Preconditions.checkNotNull(beginLocation, "Begin location can not be null"); Preconditions.checkNotNull(endLocation, "End location can not be null"); Preconditions.checkArgument( beginLocation.sourceCode.equals(endLocation.sourceCode), "Locations %s and %s come from different files; they cannot be merged", beginLocation, endLocation); Preconditions.checkArgument( beginLocation.compareTo(endLocation) <= 0, "Begin location %s must be less than or equal to end location %s", beginLocation, endLocation); return new SourceCodeLocation( beginLocation.sourceCode, beginLocation.getBeginCharacterIndex(), beginLocation.getBeginLineNumber(), beginLocation.getBeginIndexInLine(), endLocation.getEndCharacterIndex(), endLocation.getEndLineNumber(), endLocation.getEndIndexInLine()); } /** * Merges the locations of all of the given locations. If the locations span {@code SourceCode}s, * only the locations in the first {@code SourceCode} are used. If locations are out of order, * the bounding locations are used. */ public static SourceCodeLocation mergeAll(Iterable locations) { Iterator i = locations.iterator(); SourceCodeLocation loc = null; while (i.hasNext() && loc == null) { loc = i.next(); } if (!i.hasNext()) { return loc; // NOTE(flan): Many places assume that missing locations are null, not unknown. } SourceCode sourceCode = loc.sourceCode; SourceCodePoint begin = loc.begin; SourceCodePoint end = loc.end; while (i.hasNext()) { loc = i.next(); if (loc == null || loc.isUnknown() || !loc.sourceCode.equals(sourceCode)) { continue; } if (loc.begin.compareTo(begin) < 0) { begin = loc.begin; } if (loc.end.compareTo(end) > 0) { end = loc.end; } } return new SourceCodeLocation(sourceCode, begin, end); } /** * Merges the locations of all of the given locations. If the locations span {@code SourceCode}s, * only the locations in the first {@code SourceCode} are used. */ public static SourceCodeLocation merge(Iterable locations) { return mergeAll(StreamSupport.stream(locations.spliterator(), false).map(LOCATABLE_TO_LOCATION::apply).collect(Collectors.toList())); } private final SourceCode sourceCode; /** * The sequence starts at the character immediately following the begin point. */ private final SourceCodePoint begin; /** * The sequence ends at the character immediately before the end point. The * character immediately after the end point (the one indicated by the end's * {@link SourceCodePoint#characterIndex}) is not part of the sequence. The * empty sequence's begin point and end point are the same: * {@code begin.equals(end)}. */ private final SourceCodePoint end; @VisibleForTesting public SourceCodeLocation(SourceCode sourceCode, SourceCodePoint begin, SourceCodePoint end) { Preconditions.checkNotNull(sourceCode); this.sourceCode = sourceCode; this.begin = begin; this.end = end; Preconditions.checkArgument(begin.compareTo(end) <= 0, "Beginning location must come before the end location."); } public SourceCodeLocation( SourceCode sourceCode, int beginCharacterIndex, int beginLineNumber, int beginIndexInLine, int endCharacterIndex, int endLineNumber, int endIndexInLine) { this( sourceCode, new SourceCodePoint(beginCharacterIndex, beginLineNumber, beginIndexInLine), new SourceCodePoint(endCharacterIndex, endLineNumber, endIndexInLine)); } public SourceCode getSourceCode() { return sourceCode; } public boolean isUnknown() { Preconditions.checkState(begin.isUnknown() == end.isUnknown()); return begin.isUnknown(); } public int getBeginCharacterIndex() { return begin.getCharacterIndex(); } /** * The index of the line that contains the first character of the node. Indexes start at 1; 0 * means the location is not known. */ public int getBeginLineNumber() { return begin.getLineNumber(); } /** * The index of the column that contains the first character of the node. Indexes start at 1; 0 * means the location is not known. */ public int getBeginIndexInLine() { return begin.getIndexInLine(); } public int getEndCharacterIndex() { return end.getCharacterIndex(); } /** * The index of the line that contains the last character of the node. Indexes start at 1; 0 means * the location is not known. */ public int getEndLineNumber() { return end.getLineNumber(); } /** * The index of the column that comes after the last character of the node. Indexes start at 1; 0 * means the location is not known. */ public int getEndIndexInLine() { return end.getIndexInLine(); } public int getCharacterIndex() { return getBeginCharacterIndex(); } public int getLineNumber() { return getBeginLineNumber(); } public int getIndexInLine() { return getBeginIndexInLine(); } public SourceCodePoint getBegin() { return begin; } public SourceCodePoint getEnd() { return end; } @Override public boolean equals(@Nullable Object o) { if (o == null) { return false; } if (!(o instanceof SourceCodeLocation)) { return false; } SourceCodeLocation other = (SourceCodeLocation) o; return sourceCode == other.sourceCode && begin.equals(other.begin) && end.equals(other.end); } @Override public int hashCode() { return sourceCode.hashCode() ^ begin.hashCode() ^ (end.hashCode() << 16); } /** * Comparison and ordering of locations for source code in different * input files is supported because we don't always preserve * locations during AST mutations and yet we still want to be able * to sort error reports, doing the best job we can for the errors * that have known locations. For the semantics of this method, see * {@link Comparable#compareTo(Object)}. */ @Override public int compareTo(SourceCodeLocation o) { Preconditions.checkNotNull(o); if (sourceCode != o.sourceCode) { if (sourceCode == null) { return -1; } else if (o.sourceCode == null) { return 1; } else { return sourceCode.hashCode() - o.sourceCode.hashCode(); } } int startPointsComparison = begin.compareTo(o.begin); if (startPointsComparison != 0) { return startPointsComparison; } return end.compareTo(o.end); } @Override public String toString() { return String.format( "%s: [line %d, col %d -> line %d, col %d)", // half-open interval notation sourceCode.getFileName(), begin.getLineNumber(), begin.getIndexInLine(), end.getLineNumber(), end.getIndexInLine()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy