com.artemis.model.ComponentDependencyMatrix Maven / Gradle / Ivy
package com.artemis.model;
import static com.artemis.util.MatrixStringUtil.findLongestClassName;
import static com.artemis.util.MatrixStringUtil.findLongestManagerList;
import static com.artemis.util.MatrixStringUtil.findLongestSystemList;
import static com.artemis.util.MatrixStringUtil.shortName;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.artemis.model.scan.ArtemisTypeData;
import com.artemis.model.scan.ConfigurationResolver;
import com.artemis.util.ClassFinder;
import com.x5.template.Chunk;
import com.x5.template.Theme;
public class ComponentDependencyMatrix implements Opcodes {
private final File root;
private final File output;
private final String projectName;
private final ConfigurationResolver scanner;
public ComponentDependencyMatrix(String projectName, File root, File output) {
this.projectName = projectName;
this.root = root;
this.output = output;
this.scanner = new ConfigurationResolver(root);
}
public void process() {
if (scanner.components.size() == 0
&& scanner.systems.size() == 0
&& scanner.managers.size() == 0) {
String error = "No artemis classes found on classpath. "
+ "See https://github.com/junkdog/artemis-odb/wiki/Component-Dependency-Matrix for more info.";
throw new RuntimeException(error);
}
List artemisTypes = findArtemisTypes(root);
if (artemisTypes.size() == 0)
return;
// TODO: move to ColumnIndexMapping
SortedSet componentSet = findComponents(artemisTypes);
// removes any artemis classes which aren't part of artemis
scanner.clearNonDefaultTypes();
List typeMappings = new ArrayList();
for (ArtemisTypeData system : artemisTypes) {
ArtemisTypeMapping mappedType = ArtemisTypeMapping.from(
system, scanner, getComponentIndices(componentSet)); // TODO: move to outside loop
typeMappings.add(mappedType);
}
List componentColumns = new ArrayList();
for (Type component : componentSet) {
String name = component.getClassName();
name = name.substring(name.lastIndexOf('.') + 1);
componentColumns.add(name);
}
ColumnIndexMapping columnIndexMap = new ColumnIndexMapping(componentColumns, typeMappings);
for (ArtemisTypeMapping typeMapping : typeMappings) {
typeMapping.setArtemisTypeIndicies(columnIndexMap);
}
write(toMap(typeMappings), columnIndexMap);
}
public static SortedMap> toMap(List systems) {
String common = findCommonPackage(systems);
SortedMap> map = new TreeMap>();
for (int i = 0, s = systems.size(); s > i; i++) {
ArtemisTypeMapping system = systems.get(i);
String packageName = toPackageName(system.artemisType.getClassName());
packageName = (packageName.length() > common.length())
? packageName.substring(common.length())
: ".";
if (!map.containsKey(packageName))
map.put(packageName, new ArrayList());
map.get(packageName).add(system);
}
return map;
}
private static String findCommonPackage(List systems) {
String prefix = toPackageName(systems.get(0).artemisType.getClassName());
for (int i = 1, s = systems.size(); s > i; i++) {
String p = toPackageName(systems.get(i).artemisType.getClassName());
for (int j = 0, l = Math.min(prefix.length(), p.length()); l > j; j++) {
if (prefix.charAt(j) != p.charAt(j)) {
prefix = prefix.substring(0, j);
break;
}
}
}
return prefix;
}
private static String toPackageName(String className) {
return className.substring(0, className.lastIndexOf('.'));
}
private List findArtemisTypes(File root) {
List systems = new ArrayList();
for (File f : ClassFinder.find(root))
inspectType(f, systems);
Collections.sort(systems, new TypeComparator());
return systems;
}
private static SortedSet findComponents(List artemisTypes) {
SortedSet componentSet = new TreeSet(new ShortNameComparator());
for (ArtemisTypeData artemis : artemisTypes) {
componentSet.addAll(artemis.requires);
componentSet.addAll(artemis.requiresOne);
componentSet.addAll(artemis.optional);
componentSet.addAll(artemis.exclude);
}
return componentSet;
}
private void write(SortedMap> mappedSystems, ColumnIndexMapping columnIndices) {
Theme theme = new Theme();
Chunk chunk = theme.makeChunk("matrix");
List mapping = new ArrayList();
for (Entry> entry : mappedSystems.entrySet()) {
mapping.add(new ArtemisTypeMapping(entry.getKey()));
mapping.addAll(entry.getValue());
}
chunk.set("longestName", findLongestClassName(mappedSystems).replaceAll(".", "_") + "______");
chunk.set("systems", mapping);
chunk.set("headersComponents", columnIndices.componentColumns);
chunk.set("componentCount", columnIndices.componentColumns.size());
chunk.set("headersManagers", columnIndices.managerColumns);
chunk.set("managerCount", columnIndices.managerColumns.size());
chunk.set("headersSystems", columnIndices.systemColumns);
chunk.set("systemCount", columnIndices.systemColumns.size());
chunk.set("project", projectName);
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(output));
chunk.render(out);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static Map getComponentIndices(SortedSet componentSet) {
Map componentIndices = new HashMap();
int index = 0;
for (Type component : componentSet) {
componentIndices.put(component, index++);
}
return componentIndices;
}
private void inspectType(File file, List destination) {
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
ClassReader cr = new ClassReader(stream);
Type objectType = Type.getObjectType(cr.getClassName());
if (!(scanner.managers.contains(objectType) || scanner.systems.contains(objectType)))
return;
ArtemisTypeData meta = scanner.scan(cr);
meta.current = objectType;
destination.add(meta);
} catch (FileNotFoundException e) {
System.err.println("not found: " + file);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class ShortNameComparator implements Comparator {
@Override
public int compare(Type o1, Type o2) {
return shortName(o1).compareTo(shortName(o2));
}
}
private static class TypeComparator implements Comparator {
@Override
public int compare(ArtemisTypeData o1, ArtemisTypeData o2) {
return o1.current.toString().compareTo(o2.current.toString());
}
}
static class ColumnIndexMapping {
private final List componentColumns;
private final List managerColumns;
private final List systemColumns;
final Map managerIndexMap;
final Map systemIndexMap;
ColumnIndexMapping(List componentColumns, List typeMappings) {
this.componentColumns = new ArrayList(componentColumns);
managerColumns = new ArrayList();
systemColumns = new ArrayList();
managerIndexMap = new HashMap();
systemIndexMap = new HashMap();
extractArtemisTypes(typeMappings);
}
private void extractArtemisTypes(List typeMappings) {
SortedSet referencedManagers = new TreeSet();
SortedSet referencedSystems = new TreeSet();
for (ArtemisTypeMapping mapping : typeMappings) {
insert(referencedManagers, mapping.refManagers);
insert(referencedSystems, mapping.refSystems);
}
int nextColumnIndex = 0;
for (String manager : referencedManagers) {
managerIndexMap.put(manager, nextColumnIndex++);
}
nextColumnIndex = 0;
for (String system : referencedSystems) {
systemIndexMap.put(system, nextColumnIndex++);
}
managerColumns.addAll(referencedManagers);
systemColumns.addAll(referencedSystems);
}
private static void insert(SortedSet artemisSet, String[] referenced) {
for (String ref : referenced)
artemisSet.add(ref);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy