blob: 00b46b7e41b05af7bb8ea4b922914610beed6c30 [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* 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
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.imports;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Stream;
import org.eclipse.scout.sdk.core.model.api.IJavaEnvironment;
import org.eclipse.scout.sdk.core.util.JavaTypes;
import org.eclipse.scout.sdk.core.util.Strings;
public class ImportCollector implements IImportCollector {
private final IJavaEnvironment m_env;
private final Map<String/* simpleName */, ImportElement> m_imports = new HashMap<>();
private final Map<String/* simpleName */, ImportElement> m_staticImports = new HashMap<>();
public ImportCollector() {
this(null);
}
public ImportCollector(IJavaEnvironment env) {
m_env = env;
}
protected static Stream<StringBuilder> getImports(Collection<ImportElement> imports) {
return imports.stream()
.map(ImportElement::getImport);
}
protected static void addFiltered(Iterable<ImportElement> elements, boolean includeExisting, Collection<ImportElement> collector) {
for (ImportElement e : elements) {
if (!includeExisting && e.m_fromExisting) {
continue;
}
if (!e.m_static) {
if (!e.m_used) {
continue;
}
// don't create imports for java.lang.* classes
if ("java.lang".equals(e.m_packageName)) {
continue;
}
}
collector.add(e);
}
}
protected static Stream<StringBuilder> organizeImports(Iterable<ImportElement> unsortedList1, Iterable<ImportElement> unsortedList2, boolean includeExisting) {
Collection<ImportElement> workList = new TreeSet<>(new ImportComparator());
addFiltered(unsortedList1, includeExisting, workList);
addFiltered(unsortedList2, includeExisting, workList);
int lastGroup = -1;
Collection<StringBuilder> result = new ArrayList<>(workList.size() + 7 /* max number of empty group lines */);
for (ImportElement e : workList) {
if (lastGroup > 0 && lastGroup != e.m_group) {
// add empty lines for import grouping
result.add(new StringBuilder(0));
}
result.add(createImportDeclaration(e.m_static, e.getImport()));
lastGroup = e.m_group;
}
return result.stream();
}
@Override
public IJavaEnvironment getJavaEnvironment() {
return m_env;
}
@Override
public String getQualifier() {
return null;
}
@Override
public void reserveElement(TypeReferenceDescriptor cand) {
registerElementInternal(cand, false);
}
@Override
public String registerElement(TypeReferenceDescriptor cand) {
return registerElementInternal(cand, true);
}
protected String registerElementInternal(TypeReferenceDescriptor cand, boolean markAsUsed) {
ImportElement elem = m_imports.get(cand.getSimpleName());
if (elem == null) {
m_imports.put(cand.getSimpleName(), new ImportElement(false, cand.getQualifier(), cand.getSimpleName(), markAsUsed, false));
}
else {
elem.m_used = markAsUsed || elem.m_used;
}
return cand.getSimpleName();
}
@Override
public String checkExistingImports(TypeReferenceDescriptor cand) {
// handle primitive and void types
if (cand.isBaseType() || Strings.isBlank(cand.getQualifier())) {
return cand.getSimpleName();
}
ImportElement existingElem = m_imports.get(cand.getSimpleName());
if (existingElem != null && Objects.equals(existingElem.m_packageName, cand.getQualifier())) {
// already used with same package -> simple name possible
if (existingElem.m_used) {
return cand.getSimpleName();
}
return null;//must register to use simple name
}
if (existingElem != null) {
// already used with different package -> must qualify
return cand.getQualifiedName();
}
//may register the import
return null;
}
@Override
public String checkCurrentScope(TypeReferenceDescriptor cand) {
return null;
}
@Override
public void addImport(CharSequence fqn) {
addImport(fqn, false);
}
public void addImport(CharSequence fqn, boolean fromExisting) {
String packageName = JavaTypes.qualifier(fqn);
String simpleName = JavaTypes.simpleName(fqn);
m_imports.put(simpleName, new ImportElement(false, packageName, simpleName, true, fromExisting));
}
@Override
public void addStaticImport(CharSequence fqn) {
addStaticImport(fqn, false);
}
protected void addStaticImport(CharSequence fqn, boolean fromExisting) {
String packageName = JavaTypes.qualifier(fqn);
String simpleName = JavaTypes.simpleName(fqn);
m_staticImports.put(simpleName, new ImportElement(true, packageName, simpleName, true, fromExisting));
}
@Override
public Stream<StringBuilder> getImports() {
return getImports(m_imports.values());
}
@Override
public Stream<StringBuilder> getStaticImports() {
return getImports(m_staticImports.values());
}
@Override
public Stream<StringBuilder> createImportDeclarations() {
return createImportDeclarations(true);
}
@Override
public Stream<StringBuilder> createImportDeclarations(boolean includeExisting) {
return organizeImports(m_staticImports.values(), m_imports.values(), includeExisting);
}
private static final class ImportElement {
private final boolean m_static;
private final String m_packageName;
private final String m_simpleName;
private final boolean m_fromExisting;
private final int m_group;
private boolean m_used;
private ImportElement(boolean isStatic, String packageName, String simpleName, boolean used, boolean fromExisting) {
m_static = isStatic;
m_packageName = packageName;
m_simpleName = simpleName;
m_used = used;
m_fromExisting = fromExisting;
m_group = calculateGroup(isStatic, packageName);
}
private static int calculateGroup(boolean isStaticImport, String packageName) {
int factor;
if (isStaticImport) {
factor = 1;
}
else {
factor = 10;
}
if (packageName != null) {
if (packageName.startsWith("java.")) {
return factor;
}
if (packageName.startsWith("javax.")) {
return 2 * factor;
}
if (packageName.startsWith("org.")) {
return 3 * factor;
}
}
return 4 * factor;
}
public StringBuilder getImport() {
if (m_packageName == null) {
return new StringBuilder(m_simpleName);
}
return new StringBuilder(m_packageName.length() + m_simpleName.length() + 1)
.append(m_packageName).append(JavaTypes.C_DOT).append(m_simpleName);
}
}
public static StringBuilder createImportDeclaration(boolean isStatic, CharSequence fullImportExpr) {
StringBuilder b = new StringBuilder(15 + fullImportExpr.length()).append("import ");
if (isStatic) {
b.append("static ");
}
return b.append(fullImportExpr).append(';');
}
/**
* Sort in the following order: java, javax, org, other
*/
private static final class ImportComparator implements Comparator<ImportElement>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public int compare(ImportElement e1, ImportElement e2) {
int result = Integer.compare(e1.m_group, e2.m_group);
if (result != 0) {
return result;
}
result = compareStrings(e1.m_packageName, e2.m_packageName);
if (result != 0) {
return result;
}
return e1.m_simpleName.compareTo(e2.m_simpleName);
}
private static int compareStrings(@SuppressWarnings("TypeMayBeWeakened") String a, String b) {
//noinspection StringEquality
if (a == b) {
return 0;
}
if (a == null) {
return 1;
}
if (b == null) {
return -1;
}
return a.compareTo(b);
}
}
}