blob: 7a22c9e13dffec7c76da3892e708757549f3b430 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}