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

com.google.gwt.resources.gss.ImageSpriteCreator Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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 static com.google.gwt.thirdparty.common.css.compiler.passes.PassUtil.ALTERNATE;

import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.resources.client.ImageResource.RepeatStyle;
import com.google.gwt.resources.ext.ResourceContext;
import com.google.gwt.resources.ext.ResourceGeneratorUtil;
import com.google.gwt.resources.gss.ast.CssDotPathNode;
import com.google.gwt.thirdparty.common.css.SourceCodeLocation;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssCommentNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssCompilerPass;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssDeclarationNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssFunctionArgumentsNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssFunctionNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssFunctionNode.Function;
import com.google.gwt.thirdparty.common.css.compiler.ast.CssLiteralNode;
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.CssValueNode;
import com.google.gwt.thirdparty.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.gwt.thirdparty.common.css.compiler.ast.ErrorManager;
import com.google.gwt.thirdparty.common.css.compiler.ast.GssError;
import com.google.gwt.thirdparty.common.css.compiler.ast.MutatingVisitController;
import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList.Builder;
import com.google.gwt.thirdparty.guava.common.collect.Lists;

import java.util.List;

/**
 * Visitor that detects sprite definitions and replace them by several css rules in order to create
 * the corresponding sprited image.
 * 

This visitor will replace the following gss: * {@code * .foo { * padding: 5px; * gwt-sprite: imageResource; * width: 150px; * } * } * to the corresponding gss: * {@code * .foo { * padding: 5px; * /* @alternate */ width: eval("imageResource.getWidth", "px"); * /* @alternate */ height: eval("imageResource.getHeight", "px"); * /* @alternate */ overflow: hidden; * /* @alternate */ background: resourceUrl("imageResource") eval("imageResource.getLeft", * "px") eval("imageResource.getTop", "px") no-repeat; * width: 150px; * } * } *

This visitor will also check the presence of the {@link ImageOptions} annotation on the * image resource in order to support correctly horizontal or vertical repetition. */ public class ImageSpriteCreator extends DefaultTreeVisitor implements CssCompilerPass { @VisibleForTesting interface MethodByPathHelper { JMethod getMethodByPath(ResourceContext context, List pathElements, JType expectedReturnType) throws NotFoundException; } private static class MethodByPathHelperImpl implements MethodByPathHelper { @Override public JMethod getMethodByPath(ResourceContext context, List pathElements, JType expectedReturnType) throws NotFoundException { return ResourceGeneratorUtil.getMethodByPath(context.getClientBundleType(), pathElements, expectedReturnType); } } private static final String SPRITE_PROPERTY_NAME = "gwt-sprite"; private final MutatingVisitController visitController; private final ErrorManager errorManager; private final ResourceContext context; private final ImageSpriteCreator.MethodByPathHelper methodByPathHelper; private final JClassType imageResourceType; private final String resourceThisPrefix; public ImageSpriteCreator(MutatingVisitController visitController, ResourceContext context, ErrorManager errorManager) { this(visitController, context, errorManager, new MethodByPathHelperImpl()); } @VisibleForTesting ImageSpriteCreator(MutatingVisitController visitController, ResourceContext context, ErrorManager errorManager, MethodByPathHelper methodByPathHelper) { this.visitController = visitController; this.errorManager = errorManager; this.context = context; this.methodByPathHelper = methodByPathHelper; this.imageResourceType = context.getGeneratorContext().getTypeOracle().findType( ImageResource.class.getName()); this.resourceThisPrefix = context.getImplementationSimpleSourceName() + ".this"; } @Override public boolean enterDeclaration(CssDeclarationNode declaration) { String propertyName = declaration.getPropertyName().getPropertyName(); if (SPRITE_PROPERTY_NAME.equals(propertyName)) { createSprite(declaration); return true; } return super.enterDeclaration(declaration); } private void createSprite(CssDeclarationNode declaration) { List valuesNodes = declaration.getPropertyValue().getChildren(); if (valuesNodes.size() != 1) { errorManager.report(new GssError(SPRITE_PROPERTY_NAME + " must have exactly one value", declaration.getSourceCodeLocation())); return; } String imageResource = valuesNodes.get(0).getValue(); JMethod imageMethod; try { imageMethod = methodByPathHelper.getMethodByPath(context, getPathElement(imageResource), imageResourceType); } catch (NotFoundException e) { errorManager.report(new GssError("Unable to find ImageResource method " + imageResource + " in " + context.getClientBundleType().getQualifiedSourceName() + " : " + e.getMessage(), declaration.getSourceCodeLocation())); return; } ImageOptions options = imageMethod.getAnnotation(ImageOptions.class); RepeatStyle repeatStyle = options != null ? options.repeatStyle() : RepeatStyle.None; Builder listBuilder = ImmutableList.builder(); SourceCodeLocation sourceCodeLocation = declaration.getSourceCodeLocation(); String repeatText; switch (repeatStyle) { case None: repeatText = " no-repeat"; listBuilder.add(buildHeightDeclaration(imageResource, sourceCodeLocation)); listBuilder.add(buildWidthDeclaration(imageResource, sourceCodeLocation)); break; case Horizontal: repeatText = " repeat-x"; listBuilder.add(buildHeightDeclaration(imageResource, sourceCodeLocation)); break; case Vertical: repeatText = " repeat-y"; listBuilder.add(buildWidthDeclaration(imageResource, sourceCodeLocation)); break; case Both: repeatText = " repeat"; break; default: errorManager.report(new GssError("Unknown repeatStyle " + repeatStyle, sourceCodeLocation)); return; } listBuilder.add(buildOverflowDeclaration(sourceCodeLocation)); listBuilder.add(buildBackgroundDeclaration(imageResource, repeatText, sourceCodeLocation)); visitController.replaceCurrentBlockChildWith(listBuilder.build(), false); } private CssDeclarationNode buildBackgroundDeclaration(String imageResource, String repeatText, SourceCodeLocation location) { // build the url function CssFunctionNode urlFunction = new CssFunctionNode(Function.byName("url"), location); CssDotPathNode imageUrl = new CssDotPathNode(resourceThisPrefix, imageResource + ".getSafeUri" + ".asString", null, null, location); CssFunctionArgumentsNode urlFunctionArguments = new CssFunctionArgumentsNode(); urlFunctionArguments.addChildToBack(imageUrl); urlFunction.setArguments(urlFunctionArguments); // build left offset CssDotPathNode left = new CssDotPathNode(resourceThisPrefix, imageResource + ".getLeft", "-", "px", location); // build top offset CssDotPathNode top = new CssDotPathNode(resourceThisPrefix, imageResource + ".getTop", "-", "px", location); // build repeat CssLiteralNode repeat = new CssLiteralNode(repeatText, location); CssPropertyNode propertyNode = new CssPropertyNode("background", location); CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(urlFunction, left, top, repeat)); propertyValueNode.setSourceCodeLocation(location); return createDeclarationNode(propertyNode, propertyValueNode, location, true); } private CssDeclarationNode buildHeightDeclaration(String imageResource, SourceCodeLocation location) { CssPropertyNode propertyNode = new CssPropertyNode("height", location); CssValueNode valueNode = new CssDotPathNode(resourceThisPrefix, imageResource + ".getHeight", null, "px", location); CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode)); return createDeclarationNode(propertyNode, propertyValueNode, location, true); } private CssDeclarationNode buildOverflowDeclaration(SourceCodeLocation location) { CssPropertyNode propertyNode = new CssPropertyNode("overflow", location); CssValueNode valueNode = new CssLiteralNode("hidden", location); CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode)); return createDeclarationNode(propertyNode, propertyValueNode, location, true); } private CssDeclarationNode buildWidthDeclaration(String imageResource, SourceCodeLocation location) { CssPropertyNode propertyNode = new CssPropertyNode("width", location); CssValueNode valueNode = new CssDotPathNode(resourceThisPrefix, imageResource + ".getWidth", null, "px", location); CssPropertyValueNode propertyValueNode = new CssPropertyValueNode(ImmutableList.of(valueNode)); return createDeclarationNode(propertyNode, propertyValueNode, location, true); } private List getPathElement(String imageResourcePath) { return Lists.newArrayList(imageResourcePath.split("\\.")); } private CssDeclarationNode createDeclarationNode(CssPropertyNode propertyNode, CssPropertyValueNode propertyValueNode, SourceCodeLocation location, boolean useAlternate) { CssDeclarationNode replaceNode = new CssDeclarationNode(propertyNode, propertyValueNode); replaceNode.setSourceCodeLocation(location); if (useAlternate) { replaceNode.setComments(ImmutableList.of(new CssCommentNode(ALTERNATE, location))); } return replaceNode; } @Override public void runPass() { visitController.startVisit(this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy