package com.constellio.model.services.schemas.xml;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import com.constellio.data.dao.services.DataStoreTypesFactory;
import com.constellio.model.entities.Language;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.RegexConfig;
import com.constellio.model.entities.schemas.RegexConfig.RegexConfigType;
import com.constellio.model.entities.schemas.StructureFactory;
import com.constellio.model.entities.schemas.entries.CopiedDataEntry;
import com.constellio.model.entities.schemas.entries.DataEntryType;
import com.constellio.model.entities.schemas.validation.RecordMetadataValidator;
import com.constellio.model.entities.schemas.validation.RecordValidator;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.schemas.MetadataSchemasManagerRuntimeException;
import com.constellio.model.services.schemas.builders.MetadataAccessRestrictionBuilder;
import com.constellio.model.services.schemas.builders.MetadataBuilder;
import com.constellio.model.services.schemas.builders.MetadataPopulateConfigsBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypeBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder;
import com.constellio.model.utils.ClassProvider;
import com.constellio.model.utils.InstanciationUtils;
import com.constellio.model.utils.ParametrizedInstanceUtils;
public class MetadataSchemaXMLReader1 {
ClassProvider classProvider;
public MetadataSchemaXMLReader1(ClassProvider classProvider) {
this.classProvider = classProvider;
}
public MetadataSchemaTypesBuilder read(String collection, Document document, DataStoreTypesFactory typesFactory,
ModelLayerFactory modelLayerFactory) {
Element rootElement = document.getRootElement();
int version = Integer.valueOf(rootElement.getAttributeValue("version")) - 1;
MetadataSchemaTypesBuilder typesBuilder = MetadataSchemaTypesBuilder
.createWithVersion(collection, version, classProvider, Arrays.asList(Language.French));
for (Element schemaTypeElement : rootElement.getChildren("schemaType")) {
parseProfilType(typesBuilder, schemaTypeElement, typesFactory, modelLayerFactory);
}
return typesBuilder;
}
private MetadataSchemaType parseProfilType(MetadataSchemaTypesBuilder typesBuilder, Element element,
DataStoreTypesFactory typesFactory, ModelLayerFactory modelLayerFactory) {
MetadataSchemaTypeBuilder schemaTypeBuilder = typesBuilder.createNewSchemaType(getCodeValue(element), false).addLabel(
Language.French, getLabelValue(element));
schemaTypeBuilder.setSecurity(getBooleanFlagValue(element, "security"));
schemaTypeBuilder.setInTransactionLog(getBooleanFlagValueWithTrueAsDefaultValue(element, "inTransactionLog"));
parseDefaultSchema(element, schemaTypeBuilder);
parseCustomSchemas(element, schemaTypeBuilder);
return schemaTypeBuilder.build(typesFactory, modelLayerFactory);
}
private void parseCustomSchemas(Element root, MetadataSchemaTypeBuilder schemaTypeBuilder) {
Element customSchemasElement = root.getChild("customSchemas");
for (Element schemaElement : customSchemasElement.getChildren("schema")) {
parseSchema(schemaTypeBuilder, schemaElement);
}
}
private void parseSchema(MetadataSchemaTypeBuilder schemaTypeBuilder, Element schemaElement) {
MetadataSchemaBuilder schemaBuilder = schemaTypeBuilder.createCustomSchema(getCodeValue(schemaElement));
schemaBuilder.addLabel(Language.French, getLabelValue(schemaElement));
schemaBuilder.setUndeletable(getBooleanFlagValue(schemaElement, "undeletable"));
for (Element metadataElement : schemaElement.getChildren("metadata")) {
parseMetadata(schemaBuilder, metadataElement);
}
List<Element> validatorElements = parseValidators(schemaElement);
if (!validatorElements.isEmpty()) {
for (Element validatorElement : validatorElements) {
schemaBuilder.defineValidators().add(parseSchemaValidator(validatorElement));
}
}
}
private void parseMetadata(MetadataSchemaBuilder schemaBuilder, Element metadataElement) {
String codeValue = getCodeValue(metadataElement);
MetadataBuilder metadataBuilder;
if (schemaBuilder.hasMetadata(codeValue)) {
metadataBuilder = schemaBuilder.get(codeValue);
} else {
metadataBuilder = schemaBuilder.create(codeValue);
}
metadataBuilder.addLabel(Language.French, getLabelValue(metadataElement));
if (!metadataBuilder.isSystemReserved()) {
metadataBuilder.setEnabled(getBooleanFlagValue(metadataElement, "enabled"));
metadataBuilder.setDefaultRequirement(getBooleanFlagValue(metadataElement, "defaultRequirement"));
metadataBuilder.setDuplicable(false);
}
List<Element> validatorElements = parseValidators(metadataElement);
if (!validatorElements.isEmpty()) {
for (Element validatorElement : validatorElements) {
metadataBuilder.defineValidators().add(parseRecordMetadataValidator(validatorElement));
}
}
parseDataEntryElement(metadataBuilder, metadataElement);
if (!isInheriting(metadataElement)) {
if (metadataBuilder.getType() == null) {
metadataBuilder.setType(getTypeValue(metadataElement));
}
metadataBuilder.setUndeletable(getBooleanFlagValue(metadataElement, "undeletable"));
metadataBuilder.setSystemReserved(getBooleanFlagValue(metadataElement, "systemReserved"));
metadataBuilder.setEssential(getBooleanFlagValue(metadataElement, "essential"));
metadataBuilder.setEssentialInSummary(getBooleanFlagValue(metadataElement, "essentialInSummary"));
metadataBuilder.setUnmodifiable(getBooleanFlagValue(metadataElement, "unmodifiable"));
metadataBuilder.setEncrypted(getBooleanFlagValue(metadataElement, "encrypted"));
metadataBuilder.setSearchable(getBooleanFlagValue(metadataElement, "searchable"));
metadataBuilder.setSortable(getBooleanFlagValue(metadataElement, "sortable"));
metadataBuilder.setSchemaAutocomplete(getBooleanFlagValue(metadataElement, "schemaAutocomplete"));
metadataBuilder.setUniqueValue(getBooleanFlagValue(metadataElement, "uniqueValue"));
metadataBuilder.setChildOfRelationship(getBooleanFlagValue(metadataElement, "childOfRelationship"));
metadataBuilder.setTaxonomyRelationship(getBooleanFlagValue(metadataElement, "taxonomyRelationship"));
metadataBuilder.setMultivalue(getBooleanFlagValue(metadataElement, "multivalue"));
}
Class<StructureFactory> structureFactoryClass = getClassValue(metadataElement, "structureFactory");
if (structureFactoryClass != null) {
metadataBuilder.defineStructureFactory(structureFactoryClass);
}
Class<? extends Enum<?>> enumClass = getClassValue(metadataElement, "enumClass");
if (enumClass != null) {
metadataBuilder.defineAsEnum(enumClass);
}
if (!isInheriting(metadataElement)) {
setAccessRestrictions(metadataBuilder.defineAccessRestrictions(), metadataElement);
}
ParametrizedInstanceUtils utils = new ParametrizedInstanceUtils();
Object defaultValue = utils.toObject(metadataElement.getChild("defaultValue"));
if (!"null".equals(defaultValue)) {
metadataBuilder.setDefaultValue(defaultValue);
}
setPopulateConfigs(metadataBuilder, metadataElement);
addReferencesToBuilder(metadataBuilder, metadataElement);
}
private void setPopulateConfigs(MetadataBuilder metadataBuilder, Element metadataElement) {
List<String> styles = new ArrayList<>();
List<String> properties = new ArrayList<>();
List<RegexConfig> regexes = new ArrayList<>();
Element populateConfigsElement = metadataElement.getChild("populateConfigs");
if (populateConfigsElement != null) {
Element stylesElement = populateConfigsElement.getChild("styles");
Element propertiesElement = populateConfigsElement.getChild("properties");
Element regexConfigsElement = populateConfigsElement.getChild("regexConfigs");
addStringElementsToList("style", stylesElement, styles);
addStringElementsToList("property", propertiesElement, properties);
addRegexConfigElementsToList(regexConfigsElement, regexes);
}
MetadataPopulateConfigsBuilder metadataPopulateConfigsBuilder = MetadataPopulateConfigsBuilder.create();
metadataPopulateConfigsBuilder.setStyles(styles);
metadataPopulateConfigsBuilder.setProperties(properties);
metadataPopulateConfigsBuilder.setRegexes(regexes);
metadataBuilder.definePopulateConfigsBuilder(metadataPopulateConfigsBuilder);
}
private void addStringElementsToList(String childName, Element element, List<String> listToAdd) {
for (Element childElement : element.getChildren(childName)) {
listToAdd.add(childElement.getText());
}
}
private void addRegexConfigElementsToList(Element element, List<RegexConfig> listToAdd) {
for (Element regexConfigElement : element.getChildren("regexConfig")) {
String metadataInput = regexConfigElement.getChild("metadataInput").getText();
String regexPattern = regexConfigElement.getChild("regex").getText();
Pattern regex = Pattern.compile(regexPattern);
String value = regexConfigElement.getChild("value").getText();
RegexConfigType regexConfigType;
try {
regexConfigType = RegexConfigType.valueOf(regexConfigElement.getChild("regexConfigType").getText());
} catch (IllegalArgumentException | NullPointerException e) {
regexConfigType = RegexConfigType.SUBSTITUTION;
}
RegexConfig regexConfig = new RegexConfig(metadataInput, regex, value, regexConfigType);
listToAdd.add(regexConfig);
}
}
private void setAccessRestrictions(MetadataAccessRestrictionBuilder metadataAccessRestrictionBuilder,
Element metadataElement) {
Element accessRestrictionsElement = metadataElement.getChild("accessRestrictions");
Element readAccessRestrictionsElement = accessRestrictionsElement.getChild("readAccessRestrictions");
Element writeAccessRestrictionsElement = accessRestrictionsElement.getChild("writeAccessRestrictions");
Element deleteAccessRestrictionsElement = accessRestrictionsElement.getChild("deleteAccessRestrictions");
Element modifyAccessRestrictionsElement = accessRestrictionsElement.getChild("modifyAccessRestrictions");
addAccessRestrictionsRoles(readAccessRestrictionsElement, metadataAccessRestrictionBuilder.getRequiredReadRoles());
addAccessRestrictionsRoles(writeAccessRestrictionsElement, metadataAccessRestrictionBuilder.getRequiredWriteRoles());
addAccessRestrictionsRoles(deleteAccessRestrictionsElement, metadataAccessRestrictionBuilder.getRequiredDeleteRoles());
addAccessRestrictionsRoles(modifyAccessRestrictionsElement,
metadataAccessRestrictionBuilder.getRequiredModificationRoles());
}
private void addAccessRestrictionsRoles(Element rolesListElement, List<String> rolesList) {
for (Element roleElement : rolesListElement.getChildren("role")) {
rolesList.add(roleElement.getText());
}
}
@SuppressWarnings("unchecked")
private Class<? extends RecordMetadataValidator<?>> parseRecordMetadataValidator(Element validatorElement) {
try {
return classProvider.loadClass(validatorElement.getText());
} catch (ClassNotFoundException e) {
throw new MetadataSchemasManagerRuntimeException.NoSuchValidatorClass(validatorElement.getText(), e);
}
}
private List<Element> parseValidators(Element element) {
Element validatorsElements = element.getChild("validators");
return validatorsElements != null ? validatorsElements.getChildren() : new ArrayList<Element>();
}
private boolean isInheriting(Element element) {
String stringValue = element.getChild("inheriting").getText();
return "".equals(stringValue) ? null : Boolean.parseBoolean(stringValue);
}
private void addReferencesToBuilder(MetadataBuilder metadataBuilder, Element metadataElement) {
Element references = metadataElement.getChild("references");
if (metadataBuilder.getInheritance() == null && !references.getChildren().isEmpty() && !metadataBuilder
.hasDefinedReferences()) {
String schemaType = references.getChild("schemaTypes").getChildText("schemaType");
if (!"null".equals(schemaType) && StringUtils.isNotEmpty(schemaType)) {
metadataBuilder.defineReferences().setCompleteSchemaTypeCode(schemaType);
}
Element schemas = references.getChild("schemas");
for (Element schema : schemas.getChildren("schema")) {
metadataBuilder.defineReferences().addCompleteSchemaCode(schema.getText());
}
}
}
private void parseDataEntryElement(MetadataBuilder metadataBuilder, Element metadataElement) {
Element dataEntry = metadataElement.getChild("dataEntry");
if (dataEntry != null) {
String typeString = dataEntry.getChildText("dataEntryType");
DataEntryType type = DataEntryType.valueOf(typeString);
if (type == DataEntryType.MANUAL) {
metadataBuilder.defineDataEntry().asManual();
} else if (type == DataEntryType.COPIED) {
String copiedMetadata = dataEntry.getChildText("copiedMetadata");
String referenceMetadata = dataEntry.getChildText("referenceMetadata");
metadataBuilder.defineDataEntry().as(new CopiedDataEntry(referenceMetadata, copiedMetadata));
} else if (type == DataEntryType.CALCULATED) {
String calculator = dataEntry.getChildText("calculator");
metadataBuilder.defineDataEntry().asCalculated(calculator);
}
}
}
private void parseDefaultSchema(Element root, MetadataSchemaTypeBuilder schemaTypeBuilder) {
Element defaultSchemaElement = root.getChild("defaultSchema");
MetadataSchemaBuilder defaultSchemaBuilder = schemaTypeBuilder.getDefaultSchema();
defaultSchemaBuilder.addLabel(Language.French, getLabelValue(defaultSchemaElement));
for (Element metadataElement : defaultSchemaElement.getChildren("metadata")) {
parseMetadata(defaultSchemaBuilder, metadataElement);
}
List<Element> validatorElements = parseValidators(defaultSchemaElement);
if (!validatorElements.isEmpty()) {
for (Element validatorElement : validatorElements) {
defaultSchemaBuilder.defineValidators().add(parseSchemaValidator(validatorElement));
}
}
}
@SuppressWarnings("unchecked")
private Class<? extends RecordValidator> parseSchemaValidator(Element validatorElement) {
try {
return classProvider.loadClass(validatorElement.getText());
} catch (ClassNotFoundException e) {
throw new MetadataSchemasManagerRuntimeException.NoSuchValidatorClass(validatorElement.getText(), e);
}
}
private String getCodeValue(Element element) {
return element.getChild("code").getText();
}
private String getLabelValue(Element metadata) {
String labelValue = metadata.getChild("label").getText();
return "".equals(labelValue) ? null : labelValue;
}
private MetadataValueType getTypeValue(Element element) {
String stringValue = element.getChild("type").getText();
return "".equals(stringValue) ? null : MetadataValueType.valueOf(stringValue);
}
private <T> Class<T> getClassValue(Element element, String childTagName) {
String className = element.getChild(childTagName).getText();
if (StringUtils.isNotBlank(className)) {
return new InstanciationUtils().loadClassWithoutExpectableExceptions(className);
} else {
return null;
}
}
private boolean getBooleanFlagValue(Element element, String childTagName) {
Element childTag = element.getChild(childTagName);
String stringValue = childTag == null ? null : childTag.getText();
return stringValue != null && Boolean.parseBoolean(stringValue);
}
private boolean getBooleanFlagValueWithTrueAsDefaultValue(Element element, String childTagName) {
Element childTag = element.getChild(childTagName);
String stringValue = childTag == null ? null : childTag.getText();
return stringValue == null || Boolean.parseBoolean(stringValue);
}
private String getStringValue(Element element, String childTagName) {
return element.getChild(childTagName).getText();
}
}