Package com.google.gdt.eclipse.designer.nls

Source Code of com.google.gdt.eclipse.designer.nls.GwtSource

/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.gdt.eclipse.designer.nls;

import com.google.common.collect.Lists;
import com.google.gdt.eclipse.designer.common.Constants;
import com.google.gdt.eclipse.designer.model.module.ExtendPropertyElement;
import com.google.gdt.eclipse.designer.model.module.InheritsElement;
import com.google.gdt.eclipse.designer.model.module.ModuleElement;
import com.google.gdt.eclipse.designer.util.DefaultModuleProvider;
import com.google.gdt.eclipse.designer.util.DefaultModuleProvider.ModuleModification;
import com.google.gdt.eclipse.designer.util.ModuleDescription;
import com.google.gdt.eclipse.designer.util.Utils;

import org.eclipse.wb.core.model.JavaInfo;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.model.JavaInfoUtils;
import org.eclipse.wb.internal.core.model.property.GenericProperty;
import org.eclipse.wb.internal.core.nls.bundle.IPropertiesAccessor;
import org.eclipse.wb.internal.core.nls.bundle.pure.AbstractPureBundleSource;
import org.eclipse.wb.internal.core.nls.edit.IEditableSource;
import org.eclipse.wb.internal.core.nls.model.AbstractSource;
import org.eclipse.wb.internal.core.nls.model.IKeyGeneratorStrategy;
import org.eclipse.wb.internal.core.nls.model.LocaleInfo;
import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.ast.AstVisitorEx;
import org.eclipse.wb.internal.core.utils.ast.BodyDeclarationTarget;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Source for GWT Constants class.
*
* @author scheglov_ke
* @coverage gwt.nls
*/
public class GwtSource extends AbstractPureBundleSource {
  ////////////////////////////////////////////////////////////////////////////
  //
  // Static fields
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Key generator for GWT sources.
   */
  public static final IKeyGeneratorStrategy GWT_KEY_GENERATOR = new IKeyGeneratorStrategy() {
    public final String generateBaseKey(JavaInfo component, GenericProperty property) {
      String propertyTitle = property.getTitle().replace(' ', '_');
      String componentName = component.getVariableSupport().getComponentName();
      return componentName + "_" + propertyTitle;
    }
  };
  ////////////////////////////////////////////////////////////////////////////
  //
  // Possible sources
  //
  ////////////////////////////////////////////////////////////////////////////
  private static final String BUNDLE_COMMENT = "GWT variable: ";

  @Override
  protected String getBundleComment() {
    return BUNDLE_COMMENT + m_fieldName;
  }

  /**
   * Return "possible" sources that exist in given package.
   *
   * "Possible" source is source that exists in current package, but is not used in current unit. We
   * show "possible" sources only if there are no "real" sources.
   */
  public static List<AbstractSource> getPossibleSources(JavaInfo root, IPackageFragment pkg)
      throws Exception {
    List<AbstractSource> sources = Lists.newArrayList();
    for (IJavaElement packageElement : pkg.getChildren()) {
      ICompilationUnit unit = (ICompilationUnit) packageElement;
      // prepare IType
      IType type = CodeUtils.findPrimaryType(unit);
      if (type == null) {
        continue;
      }
      // check that type is is successor of Constants
      if (!CodeUtils.isSuccessorOf(type, Constants.CLASS_CONSTANTS)) {
        continue;
      }
      // prepare field name for Constants instance
      // it should be on the first line of default *.properties file
      String fieldName;
      {
        IFolder folder = (IFolder) type.getPackageFragment().getUnderlyingResource();
        IFile defaultPropertiesFile = folder.getFile(type.getElementName() + ".properties");
        if (!defaultPropertiesFile.exists()) {
          continue;
        }
        // check first line for required comment
        InputStream is = defaultPropertiesFile.getContents(true);
        String firstLine = IOUtils2.readFirstLine(is);
        if (firstLine != null && firstLine.startsWith("#" + BUNDLE_COMMENT)) {
          fieldName = firstLine.substring(1 + BUNDLE_COMMENT.length());
        } else {
          fieldName = "CONSTANTS";
        }
      }
      // OK, this is probably correct source
      try {
        String bundleName = type.getFullyQualifiedName();
        AbstractSource source = new GwtSource(root, bundleName, fieldName);
        sources.add(source);
      } catch (Throwable e) {
        DesignerPlugin.log(e);
      }
    }
    return sources;
  }

  @Override
  public void attachPossible() throws Exception {
    addField(m_root, m_bundleName, m_fieldName);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Creation
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Parse given expression and return NLSSource for it (new or existing from list).
   */
  public static AbstractSource get(JavaInfo component,
      GenericProperty property,
      Expression expression,
      List<AbstractSource> sources) throws Exception {
    ExpressionInfo expressionInfo = getExpressionInfo(component, expression);
    if (expressionInfo != null) {
      GwtSource source = getNewOrExistingSource(component, expressionInfo, sources);
      source.onKeyAdd(component, expressionInfo.m_key);
      return source;
    }
    return null;
  }

  /**
   * Find existing source with same field or create new one.
   */
  private static GwtSource getNewOrExistingSource(JavaInfo component,
      ExpressionInfo expressionInfo,
      List<AbstractSource> sources) throws Exception {
    for (AbstractSource abstractSource : sources) {
      if (abstractSource instanceof GwtSource) {
        GwtSource source = (GwtSource) abstractSource;
        if (source.m_bundleName.equals(expressionInfo.m_bundleName)) {
          return source;
        }
      }
    }
    //
    return new GwtSource(component.getRootJava(),
        expressionInfo.m_bundleName,
        expressionInfo.m_fieldName);
  }

  /**
   * Parse given expression and if it is valid GWT NLS expression, extract required information.
   */
  private static ExpressionInfo getExpressionInfo(JavaInfo component, Expression expression) {
    if (expression instanceof MethodInvocation) {
      MethodInvocation invocation = (MethodInvocation) expression;
      // check invocation
      if (invocation.arguments().size() != 0) {
        return null;
      }
      if (!(invocation.getExpression() instanceof SimpleName)) {
        return null;
      }
      //
      SimpleName field = (SimpleName) invocation.getExpression();
      String bundleName = AstNodeUtils.getFullyQualifiedName(field, true);
      String fieldName = field.getIdentifier();
      //
      SimpleName keyExpression = invocation.getName();
      String key = keyExpression.getIdentifier();
      //
      ExpressionInfo expressionInfo =
          new ExpressionInfo(expression, bundleName, fieldName, keyExpression, key);
      expression.setProperty(NLS_EXPRESSION_INFO, expressionInfo);
      return expressionInfo;
    }
    return null;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Expression information
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Information about expression. We store it in expression property to avoid parsing every time.
   */
  protected static class ExpressionInfo extends BasicExpressionInfo {
    private final String m_bundleName;
    private final String m_fieldName;

    public ExpressionInfo(Expression expression,
        String bundleName,
        String fieldName,
        Expression keyExpression,
        String key) {
      super(expression, keyExpression, key);
      m_bundleName = bundleName;
      m_fieldName = fieldName;
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Instance fields
  //
  ////////////////////////////////////////////////////////////////////////////
  private final String m_fieldName;
  private final AstEditor m_accessorEditor;

  ////////////////////////////////////////////////////////////////////////////
  //
  // Constructor
  //
  ////////////////////////////////////////////////////////////////////////////
  public GwtSource(JavaInfo root, String bundleName, String fieldName) throws Exception {
    super(root, bundleName);
    m_fieldName = fieldName;
    // prepare AST editor for Constants class
    {
      IType constants_type = m_root.getEditor().getJavaProject().findType(m_bundleName);
      ICompilationUnit constants_unit = constants_type.getCompilationUnit();
      m_accessorEditor = new AstEditor(constants_unit);
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Access
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  public String getTypeTitle() throws Exception {
    return "Constants in variable/field '" + m_fieldName + "'";
  }

  @Override
  protected IPropertiesAccessor getPropertiesAccessor() {
    return GwtPropertiesAccessor.INSTANCE;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Edit support
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  protected IKeyGeneratorStrategy getKeyGeneratorStrategy() {
    return GWT_KEY_GENERATOR;
  }

  @Override
  public void apply_addKey(String key) throws Exception {
    addKey(key);
    super.apply_addKey(key);
  }

  @Override
  protected BasicExpressionInfo apply_externalize_replaceExpression(GenericProperty property,
      String key) throws Exception {
    // replace expression
    Expression expression = property.getExpression();
    key = key.replace('.', '_');
    String newSource = m_fieldName + "." + key + "()";
    Expression newExpression = m_root.getEditor().replaceExpression(expression, newSource);
    // side effect of this invocation is that ExpressionInfo placed in newExpression
    return getExpressionInfo(m_root, newExpression);
  }

  @Override
  protected void apply_renameKeys_pre(final Map<String, String> oldToNew) throws Exception {
    m_accessorEditor.getAstUnit().accept(new AstVisitorEx() {
      @Override
      public void postVisitEx(ASTNode node) throws Exception {
        if (node instanceof MethodDeclaration) {
          MethodDeclaration methodDeclaration = (MethodDeclaration) node;
          String methodName = methodDeclaration.getName().getIdentifier();
          String newMethodName = oldToNew.get(methodName);
          if (newMethodName != null) {
            m_accessorEditor.setIdentifier(methodDeclaration.getName(), newMethodName);
          }
        }
      }
    });
    commitAccessorChanges();
  }

  @Override
  protected Expression apply_renameKey_replaceKeyExpression(AstEditor editor,
      Expression keyExpression,
      String newKey) throws Exception {
    editor.replaceInvocationName((MethodInvocation) keyExpression.getParent(), newKey);
    return keyExpression;
  }

  @Override
  protected void apply_internalizeKeys_post(final Set<String> keys) throws Exception {
    m_accessorEditor.getAstUnit().accept(new AstVisitorEx() {
      @Override
      public void postVisitEx(ASTNode node) throws Exception {
        if (node instanceof MethodDeclaration) {
          MethodDeclaration methodDeclaration = (MethodDeclaration) node;
          String methodName = methodDeclaration.getName().getIdentifier();
          if (keys.contains(methodName)) {
            m_accessorEditor.removeBodyDeclaration(methodDeclaration);
          }
        }
      }
    });
  }

  /**
   * Create NLS source for given root and parameters.
   */
  public static GwtSource apply_create(IEditableSource editable, JavaInfo root, Object o)
      throws Exception {
    // prepare parameters
    SourceParameters parameters = (SourceParameters) o;
    String fullClassName = parameters.m_constant.m_fullClassName;
    String fieldName = parameters.m_fieldName;
    // create class if it does not exist already
    if (!parameters.m_constant.m_exists) {
      // create Constants class
      {
        ensureI18NModule(root);
        // prepare class source
        String template;
        {
          template = IOUtils.toString(GwtSource.class.getResourceAsStream("newConstants.jvt"));
          template =
              StringUtils.replace(template, "%PACKAGE_NAME%", parameters.m_constant.m_packageName);
          template =
              StringUtils.replace(template, "%CLASS_NAME%", parameters.m_constant.m_className);
        }
        // create class file
        {
          String fileName = parameters.m_constant.m_className + ".java";
          createFileIfDoesNotExist(parameters.m_constant.m_packageFolder, fileName, template);
        }
      }
      // create property bundle
      {
        String propertyFileName = parameters.m_constant.m_className + ".properties";
        createPropertyBundleFile(parameters.m_constant.m_package, propertyFileName, "UTF-8");
      }
    }
    // add field
    addField(root, fullClassName, fieldName);
    // create source
    return new GwtSource(root, fullClassName, fieldName);
  }

  @Override
  public void apply_addLocale(LocaleInfo locale, Map<String, String> values) throws Exception {
    super.apply_addLocale(locale, values);
    modifyLocalesSet(locale, true);
  }

  @Override
  public void apply_removeLocale(LocaleInfo locale) throws Exception {
    super.apply_removeLocale(locale);
    modifyLocalesSet(locale, false);
  }

  @Override
  protected String getCharsetForBundleFiles() {
    return "UTF-8";
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Utils
  //
  ////////////////////////////////////////////////////////////////////////////
  private static final String I18N_MODULE = "com.google.gwt.i18n.I18N";

  /**
   * Add field with <code>Constants</code> definition.
   */
  private static void addField(JavaInfo root, String fullClassName, String fieldName)
      throws Exception {
    String code =
        "private static final "
            + fullClassName
            + " "
            + fieldName
            + " = com.google.gwt.core.client.GWT.create("
            + fullClassName
            + ".class);";
    TypeDeclaration typeDeclaration = JavaInfoUtils.getTypeDeclaration(root);
    BodyDeclarationTarget target = new BodyDeclarationTarget(typeDeclaration, true);
    root.getEditor().addFieldDeclaration(code, target);
  }

  /**
   * Ensures that module for given compilation unit contains import for module
   * "com.google.gwt.i18n.I18N".
   */
  private static void ensureI18NModule(JavaInfo root) throws Exception {
    ModuleDescription module = getModule(root);
    DefaultModuleProvider.modify(module, new ModuleModification() {
      public void modify(ModuleElement moduleElement) throws Exception {
        // check, may be already import for I18N
        for (InheritsElement inheritsElement : moduleElement.getInheritsElements()) {
          if (inheritsElement.getName().equals(I18N_MODULE)) {
            return;
          }
        }
        // import I18N
        {
          InheritsElement inheritsElement = new InheritsElement();
          moduleElement.addChild(inheritsElement);
          inheritsElement.setName(I18N_MODULE);
        }
      }
    });
  }

  /**
   * Adds or removes (depending on value of "add" parameter) given locale into module.
   */
  private void modifyLocalesSet(final LocaleInfo locale, final boolean add) throws Exception {
    // don't add default locale
    if (locale.isDefault()) {
      return;
    }
    //
    ModuleDescription module = getModule(m_root);
    DefaultModuleProvider.modify(module, new ModuleModification() {
      public void modify(ModuleElement moduleElement) throws Exception {
        // build list of locales in <extent-property name='locale'> elements, remove these elements
        List<String> locales = Lists.newArrayList();
        for (ExtendPropertyElement extendPropertyElement : moduleElement.getExtendPropertyElements()) {
          if (extendPropertyElement.getName().equals("locale")) {
            extendPropertyElement.remove();
            String[] parts = StringUtils.split(extendPropertyElement.getValues(), ", ");
            CollectionUtils.addAll(locales, parts);
          }
        }
        // add/remove new locale
        {
          String localeName = locale.getTitle();
          if (add) {
            locales.add(localeName);
          } else {
            locales.remove(localeName);
          }
        }
        // sort locales
        Collections.sort(locales);
        // add new <extent-property> with all locales
        {
          ExtendPropertyElement extendPropertyElement = new ExtendPropertyElement();
          moduleElement.addChild(extendPropertyElement);
          extendPropertyElement.setName("locale");
          extendPropertyElement.setValues(StringUtils.join(locales.iterator(), ","));
        }
      }
    });
  }

  /**
   * @return the {@link ModuleDescription}, not <code>null</code>.
   */
  private static ModuleDescription getModule(JavaInfo root) throws Exception {
    ICompilationUnit compilationUnit = root.getEditor().getModelUnit();
    return Utils.getSingleModule(compilationUnit);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Utils
  //
  ////////////////////////////////////////////////////////////////////////////
  private void addKey(String key) throws Exception {
    TypeDeclaration typeDeclaration =
        (TypeDeclaration) m_accessorEditor.getAstUnit().types().get(0);
    m_accessorEditor.addInterfaceMethodDeclaration(
        "String " + key + "()",
        new BodyDeclarationTarget(typeDeclaration, false));
    commitAccessorChanges();
  }

  /**
   * Commits changes in accessor {@link AstEditor}, including saving {@link ICompilationUnit}
   * buffer.
   */
  private void commitAccessorChanges() throws Exception {
    m_accessorEditor.commitChanges();
    m_accessorEditor.getModelUnit().save(null, true);
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.nls.GwtSource

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.