Package com.google.gwt.resources.gss

Source Code of com.google.gwt.resources.gss.ImageSpriteCreator

/*
* 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.
* <p>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 &#42;/ width: eval("imageResource.getWidth", "px");
*     /* @alternate &#42;/ height: eval("imageResource.getHeight", "px");
*     /* @alternate &#42;/ overflow: hidden;
*     /* @alternate &#42;/ background: resourceUrl("imageResource") eval("imageResource.getLeft",
*          "px") eval("imageResource.getTop", "px") no-repeat;
*     width: 150px;
*   }
* }
* <p>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<String> pathElements,
        JType expectedReturnType) throws NotFoundException;
  }

  private static class MethodByPathHelperImpl implements MethodByPathHelper {
    @Override
    public JMethod getMethodByPath(ResourceContext context, List<String> 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<CssValueNode> 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<CssDeclarationNode> 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<String> 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);
  }
}
TOP

Related Classes of com.google.gwt.resources.gss.ImageSpriteCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.