package com.constellio.model.services.schemas;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.utils.ImpossibleRuntimeException;
import com.constellio.model.entities.calculators.dependencies.Dependency;
import com.constellio.model.entities.calculators.dependencies.DynamicLocalDependency;
import com.constellio.model.entities.calculators.dependencies.LocalDependency;
import com.constellio.model.entities.calculators.dependencies.ReferenceDependency;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataSchemasRuntimeException;
import com.constellio.model.entities.schemas.MetadataSchemasRuntimeException.CannotGetMetadatasOfAnotherSchema;
import com.constellio.model.entities.schemas.MetadataSchemasRuntimeException.CannotGetMetadatasOfAnotherSchemaType;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.Schemas;
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.services.schemas.SchemaUtilsRuntimeException.SchemaUtilsRuntimeException_NoMetadataWithDatastoreCode;
import com.constellio.model.services.schemas.builders.MetadataBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaBuilderRuntimeException.NoSuchMetadata;
public class SchemaUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(SchemaUtils.class);
public static Map<String, String[]> underscoreSplitCache = new HashMap<>();
public static String[] underscoreSplitWithCache(String text) {
String[] cached = underscoreSplitCache.get(text);
if (cached == null) {
cached = text.split("_");
underscoreSplitCache.put(text, cached);
}
return cached;
}
public List<String> toMetadataLocalCodes(List<Metadata> metadatas) {
List<String> localCodes = new ArrayList<>();
for (Metadata metadata : metadatas) {
localCodes.add(this.toLocalMetadataCode(metadata.getCode()));
}
return localCodes;
}
public String toLocalMetadataCode(String codeOrLocalCode) {
String simpleCode = codeOrLocalCode;
if (codeOrLocalCode != null) {
String[] parts = underscoreSplitWithCache(codeOrLocalCode);
if (parts.length == 3) {
simpleCode = parts[2];
}
}
return simpleCode;
}
public Set<String> getLocalDependencies(Metadata metadata, List<Metadata> allMetadatas) {
Set<String> localDependencies = new HashSet<>();
if (metadata.getDataEntry().getType() == DataEntryType.COPIED) {
CopiedDataEntry dataEntry = (CopiedDataEntry) metadata.getDataEntry();
localDependencies.add(toLocalMetadataCode(dataEntry.getReferenceMetadata()));
} else if (metadata.getDataEntry().getType() == DataEntryType.CALCULATED) {
CalculatedDataEntry dataEntry = (CalculatedDataEntry) metadata.getDataEntry();
for (Dependency dependency : dataEntry.getCalculator().getDependencies()) {
if (dependency instanceof DynamicLocalDependency) {
for (Metadata aMetadata : allMetadatas) {
if (isDependentMetadata(metadata, aMetadata, (DynamicLocalDependency) dependency)) {
localDependencies.add(aMetadata.getLocalCode());
}
}
} else {
localDependencies.add(toLocalMetadataCode(dependency.getLocalMetadataCode()));
}
}
}
return localDependencies;
}
public Map<String, Set<String>> calculatedMetadataDependencies(List<Metadata> metadatas) {
Map<String, Set<String>> dependenciesMap = new HashMap<>();
for (Metadata metadata : metadatas) {
Set<String> localDependencies = getLocalDependencies(metadata, metadatas);
if (!localDependencies.isEmpty()) {
dependenciesMap.put(metadata.getLocalCode(), localDependencies);
}
}
return dependenciesMap;
}
public String getSchemaTypeCode(Metadata metadata) {
return underscoreSplitWithCache(metadata.getCode())[0];
}
public static String getSchemaTypeCode(String schema) {
return underscoreSplitWithCache(schema)[0];
}
public String getSchemaLocalCode(String schema) {
return schema.split("_")[1];
}
public String getSchemaCode(Metadata metadata) {
return getSchemaCode(metadata.getCode());
}
public String getSchemaCode(String metadataCompleteCode) {
String[] parts = underscoreSplitWithCache(metadataCompleteCode);
return parts[0] + "_" + parts[1];
}
public String getSchemaCode(MetadataBuilder builder) {
String[] parts = underscoreSplitWithCache(builder.getCode());
return parts[0] + "_" + parts[1];
}
public String getReferenceCode(Metadata automaticMetadata, ReferenceDependency<?> referenceDependency) {
String referenceCode = referenceDependency.getLocalMetadataCode();
if (!referenceCode.contains("_")) {
referenceCode = automaticMetadata.getCode().replace(automaticMetadata.getLocalCode(), referenceCode);
}
return referenceCode;
}
public String getDependencyCode(ReferenceDependency<?> referenceDependency, Metadata reference) {
String dependencyCode = referenceDependency.getDependentMetadataCode();
if (!dependencyCode.contains("_")) {
String schemaType = reference.getAllowedReferences().getAllowedSchemaType();
if (schemaType == null) {
schemaType = underscoreSplitWithCache(
reference.getAllowedReferences().getAllowedSchemas().iterator().next())[0];
}
dependencyCode = schemaType + "_default_" + dependencyCode;
}
return dependencyCode;
}
public Metadata getMetadataFromDataStoreCode(String metadataDataStoreCode, MetadataSchemaType schemaType) {
String code = getLocalCodeFromDataStoreCode(metadataDataStoreCode);
try {
return schemaType.getDefaultSchema().getMetadata(code);
} catch (NoSuchMetadata e) {
LOGGER.debug("Metadata not found in default schema, searching in ");
for (MetadataSchema customSchema : schemaType.getCustomSchemas()) {
Metadata customMetadata = customSchema.getMetadata(code);
if (customMetadata != null) {
return customMetadata;
}
}
}
throw new SchemaUtilsRuntimeException_NoMetadataWithDatastoreCode(metadataDataStoreCode);
}
public String getLocalCodeFromDataStoreCode(String metadataDataStoreCode) {
int indexOfUnderscore = metadataDataStoreCode.indexOf("_");
String firstPart;
if (indexOfUnderscore == -1) {
firstPart = metadataDataStoreCode;
} else {
firstPart = metadataDataStoreCode.substring(0, indexOfUnderscore);
}
if (firstPart.endsWith("PId")) {
return firstPart.substring(0, firstPart.length() - 3);
} else if (firstPart.endsWith("Id")) {
return firstPart.substring(0, firstPart.length() - 2);
} else {
return firstPart;
}
}
public Map<String, Metadata> buildMetadataByLocalCodeIndex(List<MetadataSchema> customSchemas,
MetadataSchema defaultSchema) {
//TODO Test that default schema metadata are returned instead of an inheritance in a custom schema
Map<String, Metadata> index = new HashMap<>();
for (MetadataSchema customSchema : customSchemas) {
index.putAll(customSchema.getIndexByLocalCode());
}
index.putAll(defaultSchema.getIndexByLocalCode());
return index;
}
public Map<String, Metadata> buildIndexByCode(List<Metadata> metadatas) {
Map<String, Metadata> index = new HashMap<>();
for (Metadata metadata : metadatas) {
index.put(metadata.getCode(), metadata);
if (metadata.isGlobal() || "code".equals(metadata.getLocalCode())) {
index.put("global_default_" + metadata.getLocalCode(), metadata);
}
if (metadata.getInheritance() != null) {
index.put(metadata.getInheritanceCode(), metadata);
}
}
return index;
}
public Map<String, Metadata> buildIndexByLocalCode(List<Metadata> metadatas) {
Map<String, Metadata> index = new HashMap<>();
for (Metadata metadata : metadatas) {
index.put(metadata.getLocalCode(), metadata);
}
return index;
}
public String getLocalCodeFromMetadataCode(String metadataCode) {
if (!metadataCode.contains("_")) {
return metadataCode;
}
String schemaCode = getSchemaCode(metadataCode);
return getLocalCode(metadataCode, schemaCode);
}
public String getLocalCode(String codeOrLocalCode, String schemaCode) {
String partialCode;
String[] codeOrLocalCodeSplitted = underscoreSplitWithCache(codeOrLocalCode);
if (codeOrLocalCodeSplitted.length == 3) {
partialCode = codeOrLocalCodeSplitted[2];
String requestedSchemaType = codeOrLocalCodeSplitted[0];
if (!Schemas.GLOBAL_SCHEMA_TYPE.equals(requestedSchemaType) && !schemaCode.startsWith(requestedSchemaType)) {
throw new CannotGetMetadatasOfAnotherSchemaType(requestedSchemaType, schemaCode);
}
String requestedSchema = codeOrLocalCodeSplitted[1];
String schemaLocalCode = underscoreSplitWithCache(schemaCode)[1];
if (!requestedSchema.equals(MetadataSchemaType.DEFAULT) && !requestedSchema.equals(schemaLocalCode)) {
throw new CannotGetMetadatasOfAnotherSchema(requestedSchema, schemaLocalCode);
}
if (codeOrLocalCodeSplitted.length != 3) {
throw new MetadataSchemasRuntimeException.InvalidCode(schemaCode);
}
} else {
partialCode = codeOrLocalCode;
}
if (partialCode.endsWith("PId")) {
partialCode = partialCode.substring(0, partialCode.length() - 3);
}
if (partialCode.endsWith("Id")) {
partialCode = partialCode.substring(0, partialCode.length() - 2);
}
return partialCode;
}
public List<String> toMetadataCodes(List<Metadata> metadatas) {
List<String> codes = new ArrayList<>();
for (Metadata metadata : metadatas) {
codes.add(metadata.getCode());
}
return codes;
}
public List<String> toSchemaTypeCodes(List<MetadataSchemaType> types) {
List<String> codes = new ArrayList<>();
for (MetadataSchemaType type : types) {
codes.add(type.getCode());
}
return codes;
}
public boolean hasSameTypeAndLocalCode(String code1, String code2) {
String typeCode1 = getSchemaTypeCode(code1);
String typeCode2 = getSchemaTypeCode(code2);
String localCode1 = getLocalCodeFromMetadataCode(code1);
String localCode2 = getLocalCodeFromMetadataCode(code2);
return typeCode1.equals(typeCode2) && localCode1.equals(localCode2);
}
public boolean isDependentMetadata(Metadata calculatedMetadata, Metadata otherMetadata,
DynamicLocalDependency dependency) {
return !calculatedMetadata.getLocalCode().equals(otherMetadata.getLocalCode())
&& (dependency.isIncludingGlobalMetadatas() || !otherMetadata.isGlobal())
&& dependency.isDependentOf(otherMetadata);
}
public static String getMetadataLocalCodeWithoutPrefix(Metadata metadata) {
//USR
//MAP
//USRMAP
//MAPUSR
String code = metadata.getLocalCode();
if (code.startsWith("MAP")) {
code = code.substring(3);
}
if (code.startsWith("USR")) {
code = code.substring(3);
}
if (code.startsWith("MAP")) {
code = code.substring(3);
}
return code;
}
static Set<String> validMetadataLocalCodes = new HashSet<>();
public static boolean isValidMetadataCodeWithCache(String localCode) {
if (localCode == null) {
return false;
}
if (validMetadataLocalCodes.contains(localCode)) {
return true;
}
String pattern = "([a-zA-Z0-9])+";
boolean valid = !localCode.matches(pattern) || (localCode.toLowerCase().endsWith("id") && !localCode.equals("id"));
if (valid) {
validMetadataLocalCodes.add(localCode);
}
return valid;
}
static Set<String> validSchemaLocalCodes = new HashSet<>();
public static boolean isValidSchemaCodeWithCache(String localCode) {
if (localCode == null) {
return false;
}
if (validMetadataLocalCodes.contains(localCode)) {
return true;
}
String pattern = "([a-zA-Z0-9])+";
boolean valid = localCode.matches(pattern);
if (valid) {
validSchemaLocalCodes.add(localCode);
}
return valid;
}
public static Metadata getMetadataUsedByCalculatedReferenceWithTaxonomyRelationship(MetadataSchema schema,
Metadata metadata) {
CalculatedDataEntry calculatedDataEntry = ((CalculatedDataEntry) metadata.getDataEntry());
for (Dependency calculatorDependency : calculatedDataEntry.getCalculator().getDependencies()) {
if (calculatorDependency instanceof LocalDependency) {
LocalDependency calculatorLocalDependency = (LocalDependency) calculatorDependency;
if (calculatorLocalDependency.getReturnType() == MetadataValueType.REFERENCE) {
Metadata otherMetadata = schema.get(calculatorLocalDependency.getLocalMetadataCode());
if (otherMetadata.getAllowedReferences().getTypeWithAllowedSchemas()
.equals(metadata.getAllowedReferences().getTypeWithAllowedSchemas())) {
return otherMetadata;
}
}
}
}
throw new ImpossibleRuntimeException("getMetadataUsedByCalculatedReferenceWithTaxonomyRelationship - No such metadata!");
}
public static List<String> localCodes(List<String> codes) {
List<String> localCodes = new ArrayList<>();
for (String code : codes) {
localCodes.add(SchemaUtils.toLocalCode(code));
}
return localCodes;
}
private static String toLocalCode(String code) {
String[] parts = new SchemaUtils().underscoreSplitWithCache(code);
return parts[2];
}
}