package org.activityinfo.geoadmin.merge;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.activityinfo.geoadmin.*;
import org.activityinfo.geoadmin.model.ActivityInfoClient;
import org.activityinfo.geoadmin.model.AdminEntity;
import org.activityinfo.geoadmin.model.AdminLevel;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
public class MergeTreeBuilder {
private static final Logger LOGGER = Logger.getLogger(MergeTreeBuilder.class.getName());
private ActivityInfoClient client;
private List<AdminLevel> parentLevels = Lists.newArrayList();
private List<AdminLevel> levels = Lists.newArrayList();
private Map<Integer, List<AdminEntity>> levelMap = Maps.newHashMap();
/**
* Map from admin level id to the name attribute index
*/
private Map<Integer, Integer> nameMap = Maps.newHashMap();
private Map<Integer, Integer> codeMap = Maps.newHashMap();
private AdminLevel level;
private ImportSource source;
public MergeTreeBuilder(ActivityInfoClient client, AdminLevel level, ImportSource source) {
this.client = client;
this.level = level;
this.source = source;
}
public MergeNode build() {
findParentLevels();
loadEntities();
findNameAttributes();
MergeNode root = new MergeNode();
if (parentLevels.isEmpty()) {
addLeafNodes(root, null, source.getFeatures());
} else {
addChildNodes(root, parentLevels.get(0), source.getFeatures());
}
return root;
}
private void loadEntities() {
loadEntities(level);
for(AdminLevel levelId : parentLevels) {
loadEntities(levelId);
}
}
private void loadEntities(AdminLevel level) {
List<AdminEntity> entities = client.getAdminEntities(level);
levels.add(level);
levelMap.put(level.getId(), entities);
}
private void findNameAttributes() {
LOGGER.info("Finding name attributes");
int numLevels = levels.size();
int numAttributes = source.getAttributeCount();
ColumnMatchMatrix matrix = new ColumnMatchMatrix(numLevels, numAttributes);
for (int i = 0; i < levels.size(); i++) {
for(AdminEntity entity : levelMap.get(levels.get(i).getId())) {
String entityName = entity.getName();
String entityCode = entity.getCode();
for(ImportFeature feature : source.getFeatures()) {
for(int j=0;j!=numAttributes;++j) {
matrix.addScore(i, j, PlaceNames.similarity(entityName, isString(feature, j)));
}
}
}
}
int[] match = matrix.solve();
for (int i = 0; i < match.length; i++) {
int bestMatch = match[i];
if(bestMatch >= 0) {
LOGGER.info("Matched name attribute for " + levels.get(i).getName() + " to " + source.getAttributeNames()[bestMatch]);
nameMap.put(levels.get(i).getId(), bestMatch);
} else {
LOGGER.info("Did not find acceptable match for name attribute for level " + level.getName());
}
}
}
private String isString(ImportFeature feature, int j) {
Object attributeValue = feature.getAttributeValue(j);
if(attributeValue instanceof String) {
return (String)attributeValue;
}
return null;
}
private void addChildNodes(MergeNode parentNode, AdminLevel adminLevel, List<ImportFeature> features) {
AdminLevel childLevel = nextParent(adminLevel);
List<AdminEntity> parents = levelMap.get(adminLevel.getId());
Joiner joiner = new Joiner(parents, features);
if(nameMap.containsKey(adminLevel.getId())) {
joiner.setNameAttributeIndex(nameMap.get(adminLevel.getId()));
}
List<AdminEntity> featureParents = joiner.joinParents();
for (AdminEntity parent : parents) {
MergeNode node = new MergeNode();
node.setLevel(level);
node.setEntity(parent);
node.setParent(parentNode);
parentNode.add(node);
List<ImportFeature> children = Lists.newArrayList();
for (int i = 0; i != features.size(); ++i) {
if (featureParents.get(i) == parent) {
children.add(features.get(i));
}
}
if (childLevel != null) {
addChildNodes(node, childLevel, children);
} else {
addLeafNodes(node, parent, children);
}
}
}
private void addLeafNodes(MergeNode parentNode, AdminEntity parent, List<ImportFeature> features) {
List<AdminEntity> entities = getChildEntities(level, parent);
Joiner joiner = new Joiner(entities, features);
List<Join> joins = joiner.joinOneToOne();
for (Join join : joins) {
MergeNode node = new MergeNode();
node.setEntity(join.getEntity());
node.setFeature(join.getFeature());
node.setLevel(level);
node.setParent(parentNode);
if (join.getEntity() != null && join.getFeature() != null) {
node.setAction(MergeAction.UPDATE);
} else {
node.setAction(MergeAction.IGNORE);
}
parentNode.add(node);
}
}
private List<AdminEntity> getChildEntities(AdminLevel level, AdminEntity parent) {
List<AdminEntity> entities = levelMap.get(level.getId());
List<AdminEntity> children = Lists.newArrayList();
for (AdminEntity child : entities) {
if (parent == null || child.getParentId() == parent.getId()) {
children.add(child);
}
}
return children;
}
private AdminLevel nextParent(AdminLevel parentLevel) {
int index = parentLevels.indexOf(parentLevel);
if (index + 1 < parentLevels.size()) {
return parentLevels.get(index + 1);
} else {
return null;
}
}
private void findParentLevels() {
Integer parentId = level.getParentId();
while (parentId != null) {
AdminLevel parentLevel = client.getAdminLevel(parentId);
parentLevels.add(0, parentLevel);
parentId = parentLevel.getParentId();
}
}
}