package com.constellio.model.entities.schemas;
import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.constellio.data.utils.KeyListMap;
import com.constellio.data.utils.KeySetMap;
import com.constellio.model.entities.calculators.dependencies.Dependency;
import com.constellio.model.entities.calculators.dependencies.LocalDependency;
import com.constellio.model.entities.calculators.dependencies.ReferenceDependency;
import com.constellio.model.entities.schemas.entries.AggregatedDataEntry;
import com.constellio.model.entities.schemas.entries.AggregationType;
import com.constellio.model.entities.schemas.entries.CalculatedDataEntry;
import com.constellio.model.entities.schemas.entries.CopiedDataEntry;
import com.constellio.model.entities.schemas.entries.DataEntryType;
import com.constellio.model.entities.schemas.entries.SequenceDataEntry;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.utils.DependencyUtils;
public class MetadataNetworkBuilder {
List<MetadataSchemaType> types;
Map<String, MetadataSchemaType> typesMap = new HashMap<>();
List<ModifiableMetadataNetworkLink> links = new ArrayList<>();
KeyListMap<String, ModifiableMetadataNetworkLink> linksFromMetadata = new KeyListMap<>();
KeyListMap<String, ModifiableMetadataNetworkLink> linksToMetadata = new KeyListMap<>();
Map<Integer, KeySetMap<String, String>> dependencies = new HashMap<>();
SchemaUtils schemaUtils = new SchemaUtils();
DependencyUtils dependencyUtils = new DependencyUtils();
public MetadataNetworkBuilder(List<MetadataSchemaType> types) {
this.types = types;
typesMap = new HashMap<>();
for (MetadataSchemaType type : types) {
typesMap.put(type.getCode(), type);
}
}
public MetadataNetwork build() {
List<MetadataNetworkLink> links = new ArrayList<>();
KeyListMap<String, MetadataNetworkLink> linksFromMetadata = new KeyListMap<>();
KeyListMap<String, MetadataNetworkLink> linksToMetadata = new KeyListMap<>();
for (ModifiableMetadataNetworkLink link : this.links) {
MetadataNetworkLink finalLink = new MetadataNetworkLink(link.fromMetadata, link.toMetadata, link.level);
links.add(finalLink);
linksFromMetadata.add(finalLink.getFromMetadata().getCode(), finalLink);
linksToMetadata.add(finalLink.getToMetadata().getCode(), finalLink);
}
return new MetadataNetwork(
Collections.unmodifiableList(links),
Collections.unmodifiableMap(linksFromMetadata.getNestedMap()),
Collections.unmodifiableMap(linksToMetadata.getNestedMap())
);
}
private Metadata idMetadataOfType(String code) {
MetadataSchemaType type = type(code);
return type == null ? null : type.getDefaultSchema().getMetadata(IDENTIFIER.getCode());
}
private MetadataSchemaType type(String code) {
return typesMap.get(code);
}
private Metadata metadata(String code) {
String schemaTypeCode = schemaUtils.getSchemaTypeCode(code);
return type(schemaTypeCode).getMetadata(code);
}
public void addNetworkLink(Metadata from, List<Metadata> tos, boolean increasingLevel) {
int level = 0;
String fromSchemaType = schemaUtils.getSchemaTypeCode(from);
for (Metadata to : tos) {
int metadataLevel = 0;
if (linksFromMetadata != null && to != null && linksFromMetadata.contains(to.getCode())) {
for (ModifiableMetadataNetworkLink link : linksFromMetadata.get(to.getCode())) {
metadataLevel = Math.max(metadataLevel, link.level);
}
String toSchemaType = schemaUtils.getSchemaTypeCode(to);
level = Math.max(metadataLevel, level);
}
}
if (increasingLevel || from.isIncreasedDependencyLevel()) {
level++;
}
for (Metadata to : tos) {
if (to != null) {
ModifiableMetadataNetworkLink link = new ModifiableMetadataNetworkLink(from, to, level);
// String message = "Adding " + link.fromMetadata.getCode() + "->" + link.toMetadata.getCode() + " [" + level + "]";
// if (message.contains("ze") || message.contains("another") || message.contains("aThird")) {
// System.out.println(message);
// }
links.add(link);
linksFromMetadata.add(from.getCode(), link);
linksToMetadata.add(to.getCode(), link);
}
}
ajustingMetadatasLevel(from, level);
}
private void ajustingMetadatasLevel(Metadata from, int level) {
for (ModifiableMetadataNetworkLink link : linksToMetadata.get(from.getCode())) {
if (link.level < level) {
link.setLevel(level);
ajustingMetadatasLevel(link.getFromMetadata(), level);
}
}
}
private KeySetMap<String, String> getDependencies(int level) {
KeySetMap<String, String> levelDependencies = this.dependencies.get(level);
if (levelDependencies == null) {
levelDependencies = new KeySetMap<>();
this.dependencies.put(level, levelDependencies);
}
return levelDependencies;
}
public static MetadataNetwork buildFrom(List<MetadataSchemaType> types) {
MetadataNetworkBuilder builder = new MetadataNetworkBuilder(types);
for (MetadataSchemaType type : types) {
for (MetadataSchema schema : type.getAllSchemas()) {
for (Metadata metadata : schema.getMetadatas()) {
if (metadata.getInheritance() == null) {
build(builder, type, schema, metadata);
}
}
}
}
return builder.build();
}
private static void build(MetadataNetworkBuilder builder, MetadataSchemaType type, MetadataSchema schema,
Metadata metadata) {
if (metadata.getLocalCode().equals("refText")) {
System.out.println("todo");
}
if (metadata.getType() == MetadataValueType.REFERENCE) {
Metadata toMetadata = builder.idMetadataOfType(metadata.getReferencedSchemaType());
if (metadata != null) {
builder.addNetworkLink(metadata, asList(toMetadata), false);
}
} else if (DataEntryType.COPIED == metadata.getDataEntry().getType()) {
CopiedDataEntry dataEntry = (CopiedDataEntry) metadata.getDataEntry();
List<Metadata> metadatas = asList(builder.metadata(dataEntry.getCopiedMetadata()),
builder.metadata(dataEntry.getReferenceMetadata()));
builder.addNetworkLink(metadata, metadatas, false);
} else if (DataEntryType.CALCULATED == metadata.getDataEntry().getType()) {
CalculatedDataEntry dataEntry = (CalculatedDataEntry) metadata.getDataEntry();
List<Metadata> metadatas = new ArrayList<>();
for (Dependency aDependency : dataEntry.getCalculator().getDependencies()) {
if (aDependency instanceof LocalDependency) {
LocalDependency dependency = (LocalDependency) aDependency;
try {
metadatas.add(schema.getMetadata(dependency.getLocalMetadataCode()));
} catch (MetadataSchemasRuntimeException.NoSuchMetadata e) {
//Metadata may be created later
}
} else if (aDependency instanceof ReferenceDependency) {
try {
ReferenceDependency dependency = (ReferenceDependency) aDependency;
Metadata refMetadata = schema.getMetadata(dependency.getLocalMetadataCode());
MetadataSchemaType referencedType = builder.type(refMetadata.getReferencedSchemaType());
Metadata dependentMetadata = referencedType.getDefaultSchema()
.getMetadata(dependency.getDependentMetadataCode());
metadatas.add(refMetadata);
metadatas.add(dependentMetadata);
} catch (MetadataSchemasRuntimeException.NoSuchMetadata e) {
//Metadata may be created later
}
}
}
builder.addNetworkLink(metadata, metadatas, false);
} else if (DataEntryType.SEQUENCE == metadata.getDataEntry().getType()) {
SequenceDataEntry dataEntry = (SequenceDataEntry) metadata.getDataEntry();
if (dataEntry.getMetadataProvidingSequenceCode() != null) {
Metadata sequenceInputMetadata = schema.getMetadata(dataEntry.getMetadataProvidingSequenceCode());
builder.addNetworkLink(metadata, asList(sequenceInputMetadata), false);
}
} else if (DataEntryType.AGGREGATED == metadata.getDataEntry().getType()) {
AggregatedDataEntry dataEntry = (AggregatedDataEntry) metadata.getDataEntry();
List<Metadata> metadatas = new ArrayList<>();
metadatas.add(builder.metadata(dataEntry.getReferenceMetadata()));
if (dataEntry.getAgregationType() == AggregationType.SUM) {
metadatas.add(builder.metadata(dataEntry.getInputMetadata()));
}
builder.addNetworkLink(metadata, metadatas, true);
}
}
}