blob: 638f378ae55db642db6a8a5711ac3f451ffcfc03 [file] [log] [blame]
/*
* Copyright 2014 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.gwt.resources.gss;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssCompilerPass;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssCompositeValueNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssDeclarationNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssFunctionNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssNumericNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssPropertyNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssPropertyValueNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssTreeVisitor;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssValueNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.gwt.thirdparty.common.css.compiler.ast.MutatingVisitController;
import com.google.gwt.thirdparty.common.css.compiler.passes.BiDiFlipper;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.util.List;
/**
* Compiler pass that BiDi flips all the flippable nodes and records if nodes have been flipped.
*/
public class RecordingBidiFlipper extends DefaultTreeVisitor implements CssCompilerPass {
/**
* This {@link MutatingVisitController} will record if an effective mutation is done.
*/
private static class RecordingMutatingVisitController implements MutatingVisitController {
private MutatingVisitController delegate;
private CssDeclarationNode visitingDeclarationNode;
private boolean hasMutation;
private RecordingMutatingVisitController(MutatingVisitController delegate) {
this.delegate = delegate;
}
@Override
public void removeCurrentNode() {
delegate.removeCurrentNode();
}
@Override
public <T extends CssNode> void replaceCurrentBlockChildWith(List<T> replacementNodes,
boolean visitTheReplacementNodes) {
// In our case, the list of replacement node should contain only one CssDeclarationNode
if (!hasMutation && visitingDeclarationNode != null && replacementNodes.size() == 1 &&
replacementNodes.get(0) instanceof CssDeclarationNode) {
CssDeclarationNode newDeclarationNode = (CssDeclarationNode) replacementNodes.get(0);
hasMutation |= isNotEqual(visitingDeclarationNode, newDeclarationNode);
}
delegate.replaceCurrentBlockChildWith(replacementNodes, visitTheReplacementNodes);
}
public void setVisitingDeclarationNode(CssDeclarationNode visitingDeclarationNode) {
this.visitingDeclarationNode = visitingDeclarationNode;
}
@Override
public void startVisit(CssTreeVisitor visitor) {
delegate.startVisit(visitor);
}
@Override
public void stopVisit() {
delegate.stopVisit();
}
private boolean compositeValueEqual(CssCompositeValueNode first, CssCompositeValueNode second) {
return valueNodeListEqual(first.getValues(), second.getValues());
}
private boolean functionNodeEqual(CssFunctionNode first, CssFunctionNode second) {
return valueNodeListEqual(first.getArguments().getChildren(), second.getArguments()
.getChildren());
}
private boolean isNotEqual(CssDeclarationNode first, CssDeclarationNode second) {
return !propertyNameEqual(first.getPropertyName(), second.getPropertyName()) ||
!propertyValuesEqual(first.getPropertyValue(), second.getPropertyValue());
}
private boolean numericNodeEqual(CssNumericNode first, CssNumericNode second) {
return Objects.equal(first.getNumericPart(), second.getNumericPart()) &&
Objects.equal(first.getUnit(), second.getUnit());
}
private boolean propertyNameEqual(CssPropertyNode first, CssPropertyNode second) {
return Objects.equal(first.getPropertyName(), second.getPropertyName());
}
private boolean propertyValuesEqual(CssPropertyValueNode first, CssPropertyValueNode second) {
return valueNodeListEqual(first.getChildren(), second.getChildren());
}
private boolean valueEqual(CssValueNode first, CssValueNode second) {
if (first.getClass() != second.getClass()) {
return false;
}
if (first instanceof CssCompositeValueNode) {
return compositeValueEqual((CssCompositeValueNode) first, (CssCompositeValueNode) second);
} else if (first instanceof CssFunctionNode) {
return functionNodeEqual((CssFunctionNode) first, (CssFunctionNode) second);
} else if (first instanceof CssNumericNode) {
return numericNodeEqual((CssNumericNode) first, (CssNumericNode) second);
} else {
return Objects.equal(first.getValue(), second.getValue());
}
}
private boolean valueNodeListEqual(List<CssValueNode> firstValues,
List<CssValueNode> secondValues) {
if (firstValues.size() != secondValues.size()) {
return false;
}
for (int i = 0; i < firstValues.size(); i++) {
CssValueNode firstNode = firstValues.get(i);
CssValueNode secondNode = secondValues.get(i);
if (!valueEqual(firstNode, secondNode)) {
return false;
}
}
return true;
}
}
private BiDiFlipper delegate;
private RecordingMutatingVisitController mutatingVisitController;
public RecordingBidiFlipper(MutatingVisitController visitController, boolean swapLtrRtlInUrl,
boolean swapLeftRightInUrl, boolean shouldFlipConstantReferences) {
this.mutatingVisitController = new RecordingMutatingVisitController(visitController);
this.delegate = new BiDiFlipper(mutatingVisitController, swapLtrRtlInUrl, swapLeftRightInUrl,
shouldFlipConstantReferences);
}
@Override
public boolean enterDeclaration(CssDeclarationNode declaration) {
mutatingVisitController.setVisitingDeclarationNode(declaration);
return delegate.enterDeclaration(declaration);
}
/**
* return true if at least one node was flipped, false otherwise.
*/
public boolean nodeFlipped() {
return mutatingVisitController.hasMutation;
}
@Override
public void runPass() {
mutatingVisitController.startVisit(this);
}
}