/* * Copyright 2013 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.compiler.passes; import com.google.common.collect.Ordering; import com.google.common.css.SourceCodeLocation; import com.google.common.css.SourceCodeLocation.SourceCodePoint; import com.google.common.css.SourceCodeLocationBuilder; import com.google.common.css.compiler.ast.CssNode; /** * A pass for finding an approximation to a minimum-size SourceCodeLocation interval that contains a * given node. * * <p>In typical CssTrees c. April 2013, most nodes do not have SourceCodeLocations, but suppose we * have such a node A with descendent nodes D_0, ... D_n that do have locations: * * <pre class="code"> * A * / \ * ----- * | \ * D_0 D_1 * </pre> * * Then we know that the subtree rooted at A includes markup beginning by * min_0^n(beginLocation(D_i)) and ending by max_0^n(endLocation(D_i)). Concretely, let * * <pre class="code"> * node getBeginCharacterIndex getEndCharacterIndex * --- --- --- * A ? ? * D_0 5 15 * D_1 17 19 * </pre> * * Then we can estimate for A: * * <pre class="code"> * A 5 19 * </pre> */ public class LocationBoundingVisitor implements UniformVisitor { private SourceCodeLocation result = null; @Override public void enter(CssNode n) { SourceCodeLocation loc = n.getSourceCodeLocation(); if (loc == null || loc.isUnknown()) { return; } if (result == null || result.isUnknown()) { result = loc; } else { Ordering<SourceCodePoint> o = Ordering.natural(); SourceCodePoint lowerBound = o.min(result.getBegin(), loc.getBegin()); SourceCodePoint upperBound = o.max(result.getEnd(), loc.getEnd()); result = new SourceCodeLocationBuilder() .setSourceCode(result.getSourceCode()) .setBeginLocation(lowerBound) .setEndLocation(upperBound) .getSourceCodeLocation(); } } @Override public void leave(CssNode node) {} public static SourceCodeLocation bound(CssNode n) { SourceCodeLocation location = n.getSourceCodeLocation(); if (location != null && !location.isUnknown()) { return location; } LocationBoundingVisitor v = new LocationBoundingVisitor(); n.getVisitController().startVisit(UniformVisitor.Adapters.asVisitor(v)); if (v.result == null) { return SourceCodeLocation.getUnknownLocation(); } return v.result; } }