/******************************************************************************* * Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package org.eclipse.xtext.xbase.ui.refactoring; import static com.google.common.collect.Lists.*; import static org.eclipse.xtext.util.Strings.*; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.viewers.StyledString; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.swt.graphics.Image; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.IGrammarAccess; import org.eclipse.xtext.common.types.xtext.ui.JdtVariableCompletions; import org.eclipse.xtext.common.types.xtext.ui.JdtVariableCompletions.CompletionDataAcceptor; import org.eclipse.xtext.common.types.xtext.ui.JdtVariableCompletions.VariableType; import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.conversion.ValueConverterException; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.xbase.XAbstractFeatureCall; import org.eclipse.xtext.xbase.XBlockExpression; import org.eclipse.xtext.xbase.XClosure; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.XFeatureCall; import org.eclipse.xtext.xbase.XMemberFeatureCall; import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver; import org.eclipse.xtext.xbase.typesystem.IExpressionScope; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; import com.google.inject.Inject; /** * @author Jan Koehnlein - Initial contribution and API */ public class NewFeatureNameUtil { @Inject private IValueConverterService valueConverterService; @Inject private IBatchTypeResolver batchTypeResolver; @Inject private JdtVariableCompletions jdtVariableCompletions; private Set<String> allKeywords; private IScope featureCallScope; @Inject public NewFeatureNameUtil(IGrammarAccess grammarAccess) { allKeywords = GrammarUtil.getAllKeywords(grammarAccess.getGrammar()); } public void setFeatureScopeContext(XExpression siblingExpression) { XBlockExpression containerBlock = (siblingExpression.eContainer() instanceof XBlockExpression) ? (XBlockExpression) siblingExpression.eContainer() : null; EObject context = siblingExpression; if (containerBlock != null && !containerBlock.getExpressions().isEmpty()) { context = containerBlock.getExpressions().get(containerBlock.getExpressions().size() - 1); } IExpressionScope expressionScope = batchTypeResolver.resolveTypes(context).getExpressionScope(context, IExpressionScope.Anchor.AFTER); featureCallScope = expressionScope.getFeatureScope(); } public void checkNewFeatureName(String newFeatureName, boolean isLookupInScope, RefactoringStatus status) { if (isEmpty(newFeatureName)) { status.addFatalError("Choose a name"); return; } try { Object value = valueConverterService.toValue(newFeatureName, "ValidID", null); valueConverterService.toString(value, "ValidID"); } catch(ValueConverterException exc) { status.addFatalError(exc.getMessage()); } if (Character.isUpperCase(newFeatureName.charAt(0))) status.addError("Discouraged name '" + newFeatureName + "'. Name should start with a lowercase letter. "); if (isKeyword(newFeatureName)) status.addFatalError("'" + newFeatureName + "' is keyword."); @SuppressWarnings("restriction") Class<?> asPrimitive = org.eclipse.xtext.common.types.access.impl.Primitives.forName(newFeatureName); if(asPrimitive != null) status.addFatalError("'" + newFeatureName + "' is reserved."); if (isLookupInScope && featureCallScope != null && isAlreadyDefined(newFeatureName)) status.addError("The name '" + newFeatureName + "' is already defined in this scope."); } protected boolean isKeyword(String newFeatureName) { return allKeywords.contains(newFeatureName); } protected boolean isAlreadyDefined(String newFeatureName) { IEObjectDescription existing = featureCallScope.getSingleElement(QualifiedName.create(newFeatureName)); return existing != null; } public String getDefaultName(XExpression expression) { String baseName = getBaseName(expression); final List<String> candidates = newArrayList(); Set<String> excludedNames = new HashSet<String>(allKeywords); for(IEObjectDescription featureDescription: featureCallScope.getAllElements()) { QualifiedName featureQName = featureDescription.getQualifiedName(); if(featureQName.getSegmentCount() == 1) excludedNames.add(featureQName.getLastSegment()); } jdtVariableCompletions.getVariableProposals(baseName, expression, VariableType.LOCAL_VAR, excludedNames, new CompletionDataAcceptor() { @Override public void accept(String replaceText, StyledString label, Image img) { candidates.add(replaceText); } }); return candidates.isEmpty() ? "dingenskirchen" : candidates.get(0); } protected String getBaseName(XExpression expression) { if (expression instanceof XMemberFeatureCall || expression instanceof XFeatureCall) { String featureName = ((XAbstractFeatureCall) expression).getFeature().getSimpleName(); if (featureName.startsWith("get")) return featureName.substring(3); else if (featureName.startsWith("is")) return featureName.substring(2); else return featureName; } if(expression instanceof XClosure) return "function"; LightweightTypeReference type = batchTypeResolver.resolveTypes(expression).getActualType(expression); if (type != null) return type.getSimpleName(); return ""; } }