package com.evolveum.midpoint.provisioning.ucf.impl.connid;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.util.QNameUtil;
import org.apache.commons.lang.StringUtils;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationalAttributeInfos;
import org.identityconnectors.framework.common.objects.Uid;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.constants.MidPointConstants;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer;
import com.evolveum.midpoint.schema.processor.ResourceAttributeContainerDefinition;
import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceSchema;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
public class ConnIdNameMapper {
private static final String CUSTOM_OBJECTCLASS_PREFIX = "Custom";
private static final String CUSTOM_OBJECTCLASS_SUFFIX = "ObjectClass";
private static final Map<String,QName> specialAttributeMapIcf = new HashMap<String,QName>();
private static final Map<QName,String> specialAttributeMapMp = new HashMap<QName,String>();
private ResourceSchema resourceSchema = null;
// Used when there is no schema (schemaless resource)
private String resourceSchemaNamespace = null;
public ConnIdNameMapper(String resourceSchemaNamespace) {
super();
this.resourceSchemaNamespace = resourceSchemaNamespace;
}
public ResourceSchema getResourceSchema() {
return resourceSchema;
}
public void setResourceSchema(ResourceSchema resourceSchema) {
this.resourceSchema = resourceSchema;
if (resourceSchema != null) {
resourceSchemaNamespace = resourceSchema.getNamespace();
}
}
private static void initialize() {
addSpecialAttributeMapping(Name.NAME, SchemaConstants.ICFS_NAME);
addSpecialAttributeMapping(Uid.NAME, SchemaConstants.ICFS_UID);
addOperationalAttributeMapping(OperationalAttributeInfos.CURRENT_PASSWORD);
addOperationalAttributeMapping(OperationalAttributeInfos.DISABLE_DATE);
addOperationalAttributeMapping(OperationalAttributeInfos.ENABLE);
addOperationalAttributeMapping(OperationalAttributeInfos.ENABLE_DATE);
addOperationalAttributeMapping(OperationalAttributeInfos.LOCK_OUT);
addOperationalAttributeMapping(OperationalAttributeInfos.PASSWORD);
addOperationalAttributeMapping(OperationalAttributeInfos.PASSWORD_EXPIRATION_DATE);
addOperationalAttributeMapping(OperationalAttributeInfos.PASSWORD_EXPIRED);
addOperationalAttributeMapping(SecretIcfOperationalAttributes.DESCRIPTION);
addOperationalAttributeMapping(SecretIcfOperationalAttributes.GROUPS);
addOperationalAttributeMapping(SecretIcfOperationalAttributes.LAST_LOGIN_DATE);
}
private static void addSpecialAttributeMapping(String icfName, QName qname) {
specialAttributeMapIcf.put(icfName, qname);
specialAttributeMapMp.put(qname, icfName);
}
private static void addOperationalAttributeMapping(
SecretIcfOperationalAttributes opAttr) {
addOperationalAttributeMapping(opAttr.getName());
}
private static void addOperationalAttributeMapping(AttributeInfo attrInfo) {
addOperationalAttributeMapping(attrInfo.getName());
}
private static void addOperationalAttributeMapping(String icfName) {
QName qName = convertUnderscoreAttributeNameToQName(icfName);
addSpecialAttributeMapping(icfName, qName);
}
public QName convertAttributeNameToQName(String icfAttrName, ResourceAttributeContainerDefinition attributesContainerDefinition) {
return convertAttributeNameToQName(icfAttrName, attributesContainerDefinition.getComplexTypeDefinition());
}
public QName convertAttributeNameToQName(String icfAttrName, ObjectClassComplexTypeDefinition ocDef) {
if (specialAttributeMapIcf.containsKey(icfAttrName)) {
for (ResourceAttributeDefinition attributeDefinition: ocDef.getAttributeDefinitions()) {
if (icfAttrName.equals(attributeDefinition.getFrameworkAttributeName())) {
return attributeDefinition.getName();
}
}
// fallback, compatibility
return specialAttributeMapIcf.get(icfAttrName);
}
QName attrXsdName = new QName(resourceSchemaNamespace, QNameUtil.escapeElementName(icfAttrName),
MidPointConstants.PREFIX_NS_RI);
return attrXsdName;
}
public QName convertAttributeNameToQName(String icfAttrName, ResourceAttributeDefinition attrDef) {
if (specialAttributeMapIcf.containsKey(icfAttrName)) {
if (icfAttrName.equals(attrDef.getFrameworkAttributeName())) {
return attrDef.getName();
}
// fallback, compatibility
return specialAttributeMapIcf.get(icfAttrName);
}
return attrDef.getName();
}
public String convertAttributeNameToIcf(ResourceAttribute<?> attribute, ObjectClassComplexTypeDefinition ocDef)
throws SchemaException {
ResourceAttributeDefinition attrDef = attribute.getDefinition();
if (attrDef == null) {
attrDef = ocDef.findAttributeDefinition(attribute.getElementName());
if (attrDef == null) {
throw new SchemaException("No attribute "+attribute.getElementName()+" in object class "+ocDef.getTypeName());
}
}
return convertAttributeNameToIcf(attrDef);
}
public <T> String convertAttributeNameToIcf(QName attributeName, ObjectClassComplexTypeDefinition ocDef, String desc)
throws SchemaException {
ResourceAttributeDefinition<T> attrDef = ocDef.findAttributeDefinition(attributeName);
if (attrDef == null) {
throw new SchemaException("No attribute "+attributeName+" in object class "+ocDef.getTypeName() + " " + desc);
}
return convertAttributeNameToIcf(attrDef);
}
public String convertAttributeNameToIcf(ResourceAttributeDefinition<?> attrDef)
throws SchemaException {
if (attrDef.getFrameworkAttributeName() != null) {
return attrDef.getFrameworkAttributeName();
}
QName attrQName = attrDef.getName();
if (specialAttributeMapMp.containsKey(attrQName)) {
return specialAttributeMapMp.get(attrQName);
}
if (!attrQName.getNamespaceURI().equals(resourceSchemaNamespace)) {
throw new SchemaException("No mapping from QName " + attrQName + " to an ICF attribute in resource schema namespace: " + resourceSchemaNamespace);
}
return attrQName.getLocalPart();
}
private boolean isUnderscoreSyntax(String icfAttrName) {
return icfAttrName.startsWith("__") && icfAttrName.endsWith("__");
}
private static QName convertUnderscoreAttributeNameToQName(String icfAttrName) {
// Strip leading and trailing underscores
String inside = icfAttrName.substring(2, icfAttrName.length()-2);
StringBuilder sb = new StringBuilder();
int lastIndex = 0;
while (true) {
int nextIndex = inside.indexOf("_", lastIndex);
if (nextIndex < 0) {
String upcase = inside.substring(lastIndex, inside.length());
sb.append(toCamelCase(upcase, lastIndex == 0));
break;
}
String upcase = inside.substring(lastIndex, nextIndex);
sb.append(toCamelCase(upcase, lastIndex == 0));
lastIndex = nextIndex + 1;
}
return new QName(SchemaConstants.NS_ICF_SCHEMA, sb.toString());
}
private static String toCamelCase(String upcase, boolean lowCase) {
if (lowCase) {
return StringUtils.lowerCase(upcase);
} else {
return StringUtils.capitalize(StringUtils.lowerCase(upcase));
}
}
/**
* Maps ICF native objectclass name to a midPoint QName objctclass name.
* <p/>
* The mapping is "stateless" - it does not keep any mapping database or any
* other state. There is a bi-directional mapping algorithm.
* <p/>
* TODO: mind the special characters in the ICF objectclass names.
*/
public QName objectClassToQname(ObjectClass icfObjectClass, String schemaNamespace, boolean legacySchema) {
if (icfObjectClass == null) {
return null;
}
if (icfObjectClass.is(ObjectClass.ALL_NAME)) {
return null;
}
if (legacySchema) {
if (icfObjectClass.is(ObjectClass.ACCOUNT_NAME)) {
return new QName(schemaNamespace, SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME,
SchemaConstants.NS_ICF_SCHEMA_PREFIX);
} else if (icfObjectClass.is(ObjectClass.GROUP_NAME)) {
return new QName(schemaNamespace, SchemaConstants.GROUP_OBJECT_CLASS_LOCAL_NAME,
SchemaConstants.NS_ICF_SCHEMA_PREFIX);
} else {
return new QName(schemaNamespace, CUSTOM_OBJECTCLASS_PREFIX + icfObjectClass.getObjectClassValue()
+ CUSTOM_OBJECTCLASS_SUFFIX, MidPointConstants.PREFIX_NS_RI);
}
} else {
return new QName(schemaNamespace, icfObjectClass.getObjectClassValue());
}
}
public ObjectClass objectClassToIcf(PrismObject<? extends ShadowType> shadow, String schemaNamespace, ConnectorType connectorType, boolean legacySchema) {
ShadowType shadowType = shadow.asObjectable();
QName qnameObjectClass = shadowType.getObjectClass();
if (qnameObjectClass == null) {
ResourceAttributeContainer attrContainer = ShadowUtil
.getAttributesContainer(shadowType);
if (attrContainer == null) {
return null;
}
ResourceAttributeContainerDefinition objectClassDefinition = attrContainer.getDefinition();
qnameObjectClass = objectClassDefinition.getTypeName();
}
return objectClassToIcf(qnameObjectClass, schemaNamespace, connectorType, legacySchema);
}
/**
* Maps a midPoint QName objctclass to the ICF native objectclass name.
* <p/>
* The mapping is "stateless" - it does not keep any mapping database or any
* other state. There is a bi-directional mapping algorithm.
* <p/>
* TODO: mind the special characters in the ICF objectclass names.
*/
public ObjectClass objectClassToIcf(ObjectClassComplexTypeDefinition objectClassDefinition, String schemaNamespace, ConnectorType connectorType, boolean legacySchema) {
QName qnameObjectClass = objectClassDefinition.getTypeName();
return objectClassToIcf(qnameObjectClass, schemaNamespace, connectorType, legacySchema);
}
public ObjectClass objectClassToIcf(QName qnameObjectClass, String schemaNamespace, ConnectorType connectorType, boolean legacySchema) {
if (!schemaNamespace.equals(qnameObjectClass.getNamespaceURI())) {
throw new IllegalArgumentException("ObjectClass QName " + qnameObjectClass
+ " is not in the appropriate namespace for "
+ connectorType + ", expected: " + schemaNamespace);
}
String lname = qnameObjectClass.getLocalPart();
if (legacySchema) {
if (SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME.equals(lname)) {
return ObjectClass.ACCOUNT;
} else if (SchemaConstants.GROUP_OBJECT_CLASS_LOCAL_NAME.equals(lname)) {
return ObjectClass.GROUP;
} else if (lname.startsWith(CUSTOM_OBJECTCLASS_PREFIX) && lname.endsWith(CUSTOM_OBJECTCLASS_SUFFIX)) {
String icfObjectClassName = lname.substring(CUSTOM_OBJECTCLASS_PREFIX.length(), lname.length()
- CUSTOM_OBJECTCLASS_SUFFIX.length());
return new ObjectClass(icfObjectClassName);
} else {
throw new IllegalArgumentException("Cannot recognize objectclass QName " + qnameObjectClass
+ " for " + ObjectTypeUtil.toShortString(connectorType) + ", expected: "
+ schemaNamespace);
}
} else {
return new ObjectClass(lname);
}
}
static {
initialize();
}
}