| /******************************************************************************* |
| * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| package org.eclipse.osbp.xtext.oxtype.imports; |
| |
| import static com.google.common.collect.Lists.newArrayList; |
| import static org.eclipse.xtext.util.Strings.equal; |
| import static org.eclipse.xtext.util.Strings.isEmpty; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.xtext.common.types.JvmDeclaredType; |
| import org.eclipse.xtext.common.types.JvmMember; |
| import org.eclipse.xtext.resource.XtextResource; |
| import org.eclipse.xtext.util.ReplaceRegion; |
| import org.eclipse.xtext.xbase.conversion.XbaseQualifiedNameValueConverter; |
| import org.eclipse.xtext.xbase.imports.ConflictResolver; |
| import org.eclipse.xtext.xbase.imports.IUnresolvedTypeResolver; |
| import org.eclipse.xtext.xbase.imports.NonOverridableTypesProvider; |
| import org.eclipse.xtext.xbase.imports.RewritableImportSection; |
| import org.eclipse.xtext.xbase.imports.TypeUsage; |
| import org.eclipse.xtext.xbase.imports.TypeUsageCollector; |
| import org.eclipse.xtext.xbase.imports.TypeUsages; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.Lists; |
| import com.google.inject.Inject; |
| import com.google.inject.Provider; |
| |
| /** |
| * A full copy of xtexts {@link org.eclipse.xtext.xbase.imports.ImportOrganizer} |
| * . This class can return the {@link OXTypeRewritableImportSection}. |
| */ |
| @SuppressWarnings("restriction") |
| public class CustomXbaseImportOrganizer { |
| |
| @Inject |
| private OXTypeRewritableImportSection.Factory importSectionFactory; |
| |
| @Inject |
| private Provider<JvmTypeEnhancingTypesCollector> typeUsageCollectorProvider; |
| |
| @Inject |
| private ConflictResolver conflictResolver; |
| |
| @Inject |
| private NonOverridableTypesProvider nonOverridableTypesProvider; |
| |
| @Inject(optional = true) |
| private IUnresolvedTypeResolver unresolvedTypeResolver; |
| |
| @Inject |
| private XbaseQualifiedNameValueConverter nameValueConverter; |
| |
| public List<ReplaceRegion> getOrganizedImportChanges(XtextResource resource) { |
| TypeUsageCollector typeUsageCollector = typeUsageCollectorProvider |
| .get(); |
| TypeUsages typeUsages = typeUsageCollector.collectTypeUsages(resource); |
| if (unresolvedTypeResolver != null) |
| unresolvedTypeResolver.resolve(typeUsages, resource); |
| Map<String, JvmDeclaredType> name2type = conflictResolver |
| .resolveConflicts(typeUsages, nonOverridableTypesProvider, |
| resource); |
| return getOrganizedImportChanges(resource, name2type, typeUsages); |
| } |
| |
| private List<ReplaceRegion> getOrganizedImportChanges( |
| XtextResource resource, |
| Map<String, JvmDeclaredType> resolvedConflicts, |
| TypeUsages typeUsages) { |
| RewritableImportSection oldImportSection = importSectionFactory |
| .parse(resource); |
| |
| RewritableImportSection newImportSection = importSectionFactory |
| .createNewEmpty(resource); |
| addImports(resolvedConflicts, typeUsages, newImportSection); |
| List<ReplaceRegion> replaceRegions = getReplacedUsageSites( |
| resolvedConflicts, typeUsages, newImportSection); |
| for (JvmMember staticImport : typeUsages.getStaticImports()) { |
| JvmDeclaredType declaringType = staticImport.getDeclaringType(); |
| if (oldImportSection.hasStaticImport(declaringType, |
| staticImport.getSimpleName(), false)) { |
| newImportSection.addStaticImport(staticImport); |
| } else { |
| newImportSection.addStaticImport(declaringType, null); |
| } |
| } |
| for (JvmMember extensionImport : typeUsages.getExtensionImports()) { |
| JvmDeclaredType declaringType = extensionImport.getDeclaringType(); |
| if (oldImportSection.hasStaticImport(declaringType, |
| extensionImport.getSimpleName(), true)) { |
| newImportSection.addStaticExtensionImport(extensionImport); |
| } else { |
| newImportSection.addStaticExtensionImport(declaringType, null); |
| } |
| } |
| replaceRegions.addAll(newImportSection.rewrite()); |
| return replaceRegions; |
| } |
| |
| public OXTypeRewritableImportSection getOrganizedImportSection( |
| XtextResource resource) { |
| TypeUsageCollector typeUsageCollector = typeUsageCollectorProvider |
| .get(); |
| TypeUsages typeUsages = typeUsageCollector.collectTypeUsages(resource); |
| if (unresolvedTypeResolver != null) |
| unresolvedTypeResolver.resolve(typeUsages, resource); |
| Map<String, JvmDeclaredType> name2type = conflictResolver |
| .resolveConflicts(typeUsages, nonOverridableTypesProvider, |
| resource); |
| return getOrganizedImportSection(resource, name2type, typeUsages); |
| } |
| |
| private OXTypeRewritableImportSection getOrganizedImportSection( |
| XtextResource resource, |
| Map<String, JvmDeclaredType> resolvedConflicts, |
| TypeUsages typeUsages) { |
| OXTypeRewritableImportSection oldImportSection = (OXTypeRewritableImportSection) importSectionFactory |
| .parse(resource); |
| |
| OXTypeRewritableImportSection newImportSection = (OXTypeRewritableImportSection) importSectionFactory |
| .createNewEmpty(resource); |
| addImports(resolvedConflicts, typeUsages, newImportSection); |
| for (JvmMember staticImport : typeUsages.getStaticImports()) { |
| JvmDeclaredType declaringType = staticImport.getDeclaringType(); |
| if (oldImportSection.hasStaticImport(declaringType, |
| staticImport.getSimpleName(), false)) { |
| newImportSection.addStaticImport(staticImport); |
| } else { |
| newImportSection.addStaticImport(declaringType, null); |
| } |
| } |
| for (JvmMember extensionImport : typeUsages.getExtensionImports()) { |
| JvmDeclaredType declaringType = extensionImport.getDeclaringType(); |
| if (oldImportSection.hasStaticImport(declaringType, |
| extensionImport.getSimpleName(), true)) { |
| newImportSection.addStaticExtensionImport(extensionImport); |
| } else { |
| newImportSection.addStaticExtensionImport(declaringType, null); |
| } |
| } |
| return newImportSection; |
| } |
| |
| private List<ReplaceRegion> getReplacedUsageSites( |
| Map<String, JvmDeclaredType> resolvedConflicts, |
| TypeUsages typeUsages, RewritableImportSection newImportSection) { |
| List<ReplaceRegion> result = newArrayList(); |
| for (Map.Entry<String, JvmDeclaredType> textToType : resolvedConflicts |
| .entrySet()) { |
| getReplacedUsagesOf(textToType, typeUsages, newImportSection, |
| result); |
| } |
| return result; |
| } |
| |
| private void getReplacedUsagesOf( |
| Map.Entry<String, JvmDeclaredType> nameToType, |
| TypeUsages typeUsages, RewritableImportSection importSection, |
| List<ReplaceRegion> result) { |
| String nameToUse = nameToType.getKey(); |
| JvmDeclaredType type = nameToType.getValue(); |
| String packageLocalName = getPackageLocalName(type); |
| for (TypeUsage typeUsage : typeUsages.getUsages(type)) { |
| ReplaceRegion replaceRegion = getReplaceRegion(nameToUse, |
| packageLocalName, type, typeUsage, importSection); |
| if (replaceRegion != null) { |
| result.add(replaceRegion); |
| } |
| } |
| } |
| |
| /* @Nullable */ |
| private ReplaceRegion getReplaceRegion(String nameToUse, |
| String packageLocalName, JvmDeclaredType type, TypeUsage usage, |
| RewritableImportSection importSection) { |
| // if the resource contains two types with the same simple name, we |
| // don't add any import |
| // but we can still use the package local name within the same package. |
| if (equal(usage.getContextPackageName(), type.getPackageName())) { |
| if (type.eContainer() != null) { |
| String declarationLocalName = getLocalName(type, |
| usage.getContext()); |
| nameToUse = declarationLocalName; |
| } else if (importSection.getImportedTypes(packageLocalName) == null) { |
| nameToUse = packageLocalName; |
| } |
| } |
| String textToUse = getConcreteSyntax(nameToUse, type, usage); |
| return new ReplaceRegion(usage.getTextRegion(), textToUse); |
| } |
| |
| private String getLocalName(JvmDeclaredType type, JvmMember context) { |
| JvmMember containerCandidate = context; |
| while (containerCandidate != null) { |
| if (containerCandidate == type) { |
| return type.getSimpleName(); |
| } else if (EcoreUtil.isAncestor(containerCandidate, type)) { |
| String contextName = containerCandidate.getQualifiedName('.'); |
| String typeName = type.getQualifiedName('.'); |
| return typeName.substring(contextName.length() + 1); |
| } |
| EObject container = containerCandidate.eContainer(); |
| if (container instanceof JvmMember) { |
| containerCandidate = (JvmMember) container; |
| } else { |
| return null; |
| } |
| } |
| return null; |
| } |
| |
| private String getConcreteSyntax(String name, JvmDeclaredType importedType, |
| TypeUsage usage) { |
| JvmDeclaredType usedType = usage.getUsedType(); |
| if (usedType == null) { |
| String typeName = usage.getUsedTypeName(); |
| String suffix = getSuffix(usage); |
| String fullTypeName = typeName + suffix; |
| return nameValueConverter.toString(fullTypeName); |
| } else { |
| if (usedType != importedType) { |
| List<String> segments = Lists.newLinkedList(); |
| while (usedType != importedType) { |
| segments.add(0, usedType.getSimpleName()); |
| usedType = usedType.getDeclaringType(); |
| } |
| name = name + '.' + Joiner.on('.').join(segments); |
| } else { |
| String suffix = getSuffix(usage); |
| name = name + suffix; |
| } |
| return nameValueConverter.toString(name); |
| } |
| } |
| |
| protected String getSuffix(TypeUsage usage) { |
| String suffix = usage.getSuffix(); |
| suffix = suffix.replace('$', '.').replace("::", "."); |
| return suffix; |
| } |
| |
| private void addImports(Map<String, JvmDeclaredType> resolvedConflicts, |
| TypeUsages typeUsages, RewritableImportSection target) { |
| for (Map.Entry<String, JvmDeclaredType> entry : resolvedConflicts |
| .entrySet()) { |
| String text = entry.getKey(); |
| JvmDeclaredType type = entry.getValue(); |
| Iterable<TypeUsage> usages = typeUsages.getUsages(type); |
| if (needsImport(type, text, nonOverridableTypesProvider, usages)) { |
| target.addImport(type); |
| } |
| } |
| } |
| |
| protected String getPackageLocalName(JvmDeclaredType type) { |
| String packageName = type.getPackageName(); |
| if (isEmpty(packageName)) |
| return type.getQualifiedName('.'); |
| else |
| return type.getQualifiedName('.').substring( |
| packageName.length() + 1); |
| } |
| |
| protected boolean needsImport(JvmDeclaredType type, String name, |
| NonOverridableTypesProvider nonOverridableTypesProvider, |
| Iterable<TypeUsage> usages) { |
| boolean nameEquals = type.getQualifiedName().equals(name) |
| || type.getQualifiedName('.').equals(name); |
| return !(nameEquals || isUsedInLocalContextOnly(type, usages, |
| nonOverridableTypesProvider, name)); |
| } |
| |
| protected boolean isUsedInLocalContextOnly(JvmDeclaredType type, |
| Iterable<TypeUsage> usages, |
| NonOverridableTypesProvider nonOverridableTypesProvider, String name) { |
| for (TypeUsage usage : usages) { |
| if (nonOverridableTypesProvider.getVisibleType(usage.getContext(), |
| name) == null |
| && !equal(usage.getContextPackageName(), |
| type.getPackageName())) |
| return false; |
| } |
| return true; |
| } |
| } |