package gw.systables.verifier uses gw.api.locale.DisplayKey uses com.guidewire.pc.domain.productmodel.type.skeleton.XMLNodeBundle uses gw.internal.ext.org.apache.commons.collections.keyvalue.MultiKey uses gw.xml.parser2.PLXMLNode uses java.util.Map uses gw.entity.IEntityPropertyInfo uses gw.entity.IEntityType uses com.guidewire.pl.system.database.DatabaseDependencies uses com.guidewire.pl.system.database.metadata.Index uses gw.lang.reflect.TypeSystem uses java.util.Set uses java.util.List @ReadOnly class UniqueIndexVerifier extends VerifierBase { override function verify(importNode : PLXMLNode) : Map>{ var results : Map> = {} var bundle = new XMLNodeBundle() for (typeNodesEntry in partitionNodesByType(importNode).entrySet()) { for (indexPropertiesEntry in getUniqueIndexes(typeNodesEntry.getKey()).entrySet()) { var values : Set = {} for (node in typeNodesEntry.Value) { var value = new MultiKey(indexPropertiesEntry.Value.map(\epi -> bundle.getPropertyValue(node, epi)).toArray()) if (values.contains(value)) { addErrorToResults( DisplayKey.get("Java.ValidationError.SystemTables.UniqueIndexConstraintViolation", indexPropertiesEntry.Value, value.Keys.toList()), node, results) } else { values.add(value) } } } } return results } /** * @param type The type of system table entity to evaluate * @return All unique indexes for the given system table type where * there is no column understood to be auto-generated by the DB, such as ID, * and ignores RetiredValue in the list of properties. */ private function getUniqueIndexes(type : IEntityType) : Map> { var uniqueIndexes : Map> = {} var table = DatabaseDependencies.getTableMetadataFactory().getTableMetadata(type) var indexes = table.Indexes.where(\i -> i.Unique) for (i in indexes) { var columns = i.Columns var columnInfosForIndex : List = {} // we only want indexes without ID, which is auto-generated and totally out of our control // since we don't want access to the DB if (!columns.hasMatch(\c -> c.DBColumn.PrimaryProperty.Property.Name == "ID")) { // we want to exclude RetiredValue from our calculations, since being out of the DB, we don't care columnInfosForIndex.addAll(columns.map(\c -> c.DBColumn.PrimaryProperty.Property).where(\p -> p.Name != "RetiredValue")) uniqueIndexes.put(i, columnInfosForIndex) } } return uniqueIndexes } /** * @param importNode The root node of the file * @return All the nodes split up by the IEntityType implied by the * element name of each child node. */ private function partitionNodesByType(importNode : PLXMLNode) : Map> { var nodesByElementName = importNode.Children.partition(\n -> n.ElementName) var types = nodesByElementName.Keys.partitionUniquely(\k -> TypeSystem.getByRelativeName(k) as IEntityType) return types.mapValues(\tn -> nodesByElementName[tn]) } }