/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.schema.processor;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;
import com.evolveum.midpoint.prism.ComplexTypeDefinition;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.schema.SchemaDefinitionFactory;
import com.evolveum.midpoint.prism.schema.SchemaProcessorUtil;
import com.evolveum.midpoint.prism.schema.SchemaToDomProcessor;
import com.evolveum.midpoint.schema.constants.MidPointConstants;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.DisplayableValue;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.sun.xml.xsom.XSAnnotation;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSParticle;
import java.util.Collection;
/**
* @author semancik
*
*/
public class MidPointSchemaDefinitionFactory extends SchemaDefinitionFactory {
@Override
public ComplexTypeDefinition createComplexTypeDefinition(XSComplexType complexType,
PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
if (isResourceObject(annotation)) {
return createObjectClassDefinition(complexType, prismContext, annotation);
}
return super.createComplexTypeDefinition(complexType, prismContext, annotation);
}
private ComplexTypeDefinition createObjectClassDefinition(XSComplexType complexType,
PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
QName typeName = new QName(complexType.getTargetNamespace(),complexType.getName());
ObjectClassComplexTypeDefinitionImpl ocDef = new ObjectClassComplexTypeDefinitionImpl(typeName, prismContext);
// nativeObjectClass
Element nativeAttrElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_NATIVE_OBJECT_CLASS);
String nativeObjectClass = nativeAttrElement == null ? null : nativeAttrElement.getTextContent();
ocDef.setNativeObjectClass(nativeObjectClass);
ShadowKindType kind = null;
Element kindElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_KIND);
if (kindElement != null) {
String kindString = kindElement.getTextContent();
if (StringUtils.isEmpty(kindString)) {
throw new SchemaException("The definition of kind is empty in the annotation of object class "+typeName);
}
try {
kind = ShadowKindType.fromValue(kindString);
} catch (IllegalArgumentException e) {
throw new SchemaException("Definition of unknown kind '"+kindString+"' in the annotation of object class "+typeName);
}
ocDef.setKind(kind);
}
Boolean defaultInAKind = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, MidPointConstants.RA_DEFAULT);
if (defaultInAKind == null) {
ocDef.setDefaultInAKind(false);
} else {
ocDef.setDefaultInAKind(defaultInAKind);
}
Boolean auxiliary = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, MidPointConstants.RA_AUXILIARY);
if (auxiliary == null) {
ocDef.setAuxiliary(false);
} else {
ocDef.setAuxiliary(auxiliary);
}
String intent = null;
Element intentElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_INTENT);
if (intentElement != null) {
intent = intentElement.getTextContent();
if (StringUtils.isEmpty(intent)) {
throw new SchemaException("The definition of intent is empty in the annotation of object class "+typeName);
}
ocDef.setIntent(intent);
}
// accountType: DEPRECATED
if (isAccountObject(annotation)) {
if (kind != null && kind != ShadowKindType.ACCOUNT) {
throw new SchemaException("Conflicting definition of kind and legacy account annotation in the annotation of object class "+typeName);
}
ocDef.setKind(ShadowKindType.ACCOUNT);
Element account = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_ACCOUNT);
if (account != null && !defaultInAKind) {
String defaultValue = account.getAttribute("default");
// Compatibility (DEPRECATED)
if (defaultValue != null) {
ocDef.setDefaultInAKind(Boolean.parseBoolean(defaultValue));
}
}
Element accountTypeElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_ACCOUNT_TYPE);
if (accountTypeElement != null) {
String accountType = accountTypeElement.getTextContent();
if (intent != null && !intent.equals(accountType)) {
throw new SchemaException("Conflicting definition of intent and legacy accountType annotation in the annotation of object class "+typeName);
}
ocDef.setIntent(accountType);
}
}
return ocDef;
}
@Override
public void finishComplexTypeDefinition(ComplexTypeDefinition complexTypeDefinition, XSComplexType complexType,
PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
super.finishComplexTypeDefinition(complexTypeDefinition, complexType, prismContext, annotation);
if (complexTypeDefinition instanceof ObjectClassComplexTypeDefinition) {
// TODO is this safe?
finishObjectClassDefinition((ObjectClassComplexTypeDefinitionImpl)complexTypeDefinition, complexType, prismContext, annotation);
}
}
private void finishObjectClassDefinition(ObjectClassComplexTypeDefinitionImpl ocDef,
XSComplexType complexType, PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
// displayNameAttribute
ResourceAttributeDefinition attrDefinition = getAnnotationReference(annotation, MidPointConstants.RA_DISPLAY_NAME_ATTRIBUTE, ocDef);
if (attrDefinition != null) {
ocDef.setDisplayNameAttribute(attrDefinition);
}
// namingAttribute
attrDefinition = getAnnotationReference(annotation, MidPointConstants.RA_NAMING_ATTRIBUTE, ocDef);
if (attrDefinition != null) {
ocDef.setNamingAttribute(attrDefinition);
}
// descriptionAttribute
attrDefinition = getAnnotationReference(annotation, MidPointConstants.RA_DESCRIPTION_ATTRIBUTE, ocDef);
if (attrDefinition != null) {
ocDef.setDescriptionAttribute(attrDefinition);
}
// identifier
attrDefinition = getAnnotationReference(annotation, MidPointConstants.RA_IDENTIFIER, ocDef);
if (attrDefinition != null) {
((Collection)ocDef.getPrimaryIdentifiers()).add(attrDefinition);
}
// secondaryIdentifier
attrDefinition = getAnnotationReference(annotation, MidPointConstants.RA_SECONDARY_IDENTIFIER, ocDef);
if (attrDefinition != null) {
((Collection)ocDef.getSecondaryIdentifiers()).add(attrDefinition);
}
}
@Override
public void addExtraComplexTypeAnnotations(ComplexTypeDefinition definition, Element appinfo, SchemaToDomProcessor schemaToDomProcessor) {
super.addExtraComplexTypeAnnotations(definition, appinfo, schemaToDomProcessor);
if (definition instanceof ObjectClassComplexTypeDefinition) {
addExtraObjectClassAnnotations((ObjectClassComplexTypeDefinition)definition, appinfo, schemaToDomProcessor);
}
}
private void addExtraObjectClassAnnotations(ObjectClassComplexTypeDefinition definition, Element appinfo, SchemaToDomProcessor processor) {
processor.addAnnotation(MidPointConstants.RA_RESOURCE_OBJECT, appinfo);
// displayName, identifier, secondaryIdentifier
for (ResourceAttributeDefinition identifier : definition.getPrimaryIdentifiers()) {
processor.addRefAnnotation(MidPointConstants.RA_IDENTIFIER, identifier.getName(), appinfo);
}
for (ResourceAttributeDefinition identifier : definition.getSecondaryIdentifiers()) {
processor.addRefAnnotation(MidPointConstants.RA_SECONDARY_IDENTIFIER,identifier.getName(),appinfo);
}
if (definition.getDisplayNameAttribute() != null) {
processor.addRefAnnotation(MidPointConstants.RA_DISPLAY_NAME_ATTRIBUTE, definition.getDisplayNameAttribute().getName(), appinfo);
}
if (definition.getDescriptionAttribute() != null) {
processor.addRefAnnotation(MidPointConstants.RA_DESCRIPTION_ATTRIBUTE, definition.getDescriptionAttribute().getName(), appinfo);
}
if (definition.getNamingAttribute() != null) {
processor.addRefAnnotation(MidPointConstants.RA_NAMING_ATTRIBUTE, definition.getNamingAttribute().getName(), appinfo);
}
// TODO: what to do with native object class, composite
// // nativeObjectClass
if (!StringUtils.isEmpty(definition.getNativeObjectClass())) {
processor.addAnnotation(MidPointConstants.RA_NATIVE_OBJECT_CLASS, definition.getNativeObjectClass(), appinfo);
}
// kind
if (definition.getKind() != null) {
processor.addAnnotation(MidPointConstants.RA_KIND, definition.getKind().value(), appinfo);
}
if (definition.isDefaultInAKind()) {
processor.addAnnotation(MidPointConstants.RA_DEFAULT, true, appinfo);
}
if (definition.isAuxiliary()) {
processor.addAnnotation(MidPointConstants.RA_AUXILIARY, true, appinfo);
}
if (definition.getIntent() != null) {
processor.addAnnotation(MidPointConstants.RA_INTENT, definition.getIntent(), appinfo);
}
}
@Override
public <C extends Containerable> PrismContainerDefinition<C> createExtraDefinitionFromComplexType(XSComplexType complexType,
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
// if (complexTypeDefinition instanceof ObjectClassComplexTypeDefinition) {
// return createResourceAttributeContainerDefinition(complexType, (ObjectClassComplexTypeDefinition)complexTypeDefinition,
// prismContext, annotation);
// }
return super.createExtraDefinitionFromComplexType(complexType, complexTypeDefinition, prismContext, annotation);
}
private PrismContainerDefinition createResourceAttributeContainerDefinition(XSComplexType complexType,
ObjectClassComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, XSAnnotation annotation) {
ResourceAttributeContainerDefinition attrContDef = new ResourceAttributeContainerDefinitionImpl(null, complexTypeDefinition, prismContext);
return attrContDef;
}
@Override
public <T> PrismPropertyDefinition<T> createPropertyDefinition(QName elementName, QName typeName,
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, XSAnnotation annotation,
XSParticle elementParticle) throws SchemaException {
if (complexTypeDefinition != null && complexTypeDefinition instanceof ObjectClassComplexTypeDefinition) {
return createResourceAttributeDefinition(elementName, typeName, prismContext, annotation);
}
return super.createPropertyDefinition(elementName, typeName, complexTypeDefinition, prismContext, annotation, elementParticle);
}
@Override
public <T> PrismPropertyDefinition<T> createPropertyDefinition(QName elementName, QName typeName,
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, XSAnnotation annotation,
XSParticle elementParticle, Collection<? extends DisplayableValue<T>> allowedValues, T defaultValue) throws SchemaException {
if (complexTypeDefinition != null && complexTypeDefinition instanceof ObjectClassComplexTypeDefinition) {
return createResourceAttributeDefinition(elementName, typeName, prismContext, annotation);
}
return super.createPropertyDefinition(elementName, typeName, complexTypeDefinition, prismContext, annotation, elementParticle, allowedValues, defaultValue);
}
private PrismPropertyDefinition createResourceAttributeDefinition(QName elementName, QName typeName,
PrismContext prismContext, XSAnnotation annotation) throws SchemaException {
ResourceAttributeDefinitionImpl attrDef = new ResourceAttributeDefinitionImpl(elementName, typeName, prismContext);
// nativeAttributeName
Element nativeAttrElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_NATIVE_ATTRIBUTE_NAME);
String nativeAttributeName = nativeAttrElement == null ? null : nativeAttrElement.getTextContent();
if (!StringUtils.isEmpty(nativeAttributeName)) {
attrDef.setNativeAttributeName(nativeAttributeName);
}
// frameworkAttributeName
Element frameworkAttrElement = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_FRAMEWORK_ATTRIBUTE_NAME);
String frameworkAttributeName = frameworkAttrElement == null ? null : frameworkAttrElement.getTextContent();
if (!StringUtils.isEmpty(frameworkAttributeName)) {
attrDef.setFrameworkAttributeName(frameworkAttributeName);
}
// returnedByDefault
attrDef.setReturnedByDefault(SchemaProcessorUtil.getAnnotationBoolean(annotation, MidPointConstants.RA_RETURNED_BY_DEFAULT_NAME));
return attrDef;
}
@Override
public void addExtraPropertyAnnotations(PrismPropertyDefinition definition, Element appinfo,
SchemaToDomProcessor schemaToDomProcessor) {
super.addExtraPropertyAnnotations(definition, appinfo, schemaToDomProcessor);
if (definition instanceof ResourceAttributeDefinition) {
ResourceAttributeDefinition rad = (ResourceAttributeDefinition)definition;
if (rad.getNativeAttributeName() != null) {
schemaToDomProcessor.addAnnotation(MidPointConstants.RA_NATIVE_ATTRIBUTE_NAME, rad.getNativeAttributeName(), appinfo);
}
if (rad.getFrameworkAttributeName() != null) {
schemaToDomProcessor.addAnnotation(MidPointConstants.RA_FRAMEWORK_ATTRIBUTE_NAME, rad.getFrameworkAttributeName(), appinfo);
}
if (rad.getReturnedByDefault() != null) {
schemaToDomProcessor.addAnnotation(MidPointConstants.RA_RETURNED_BY_DEFAULT_NAME, rad.getReturnedByDefault().toString(), appinfo);
}
}
}
private boolean isResourceObject(XSAnnotation annotation) {
// annotation: resourceObject
if (SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_RESOURCE_OBJECT) != null) {
return true;
}
// annotation: accountType DEPRECATED
if (SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_ACCOUNT) != null) {
// <accountType> implies <resourceObject> ... at least for now (compatibility)
return true;
}
return false;
}
private boolean isAccountObject(XSAnnotation annotation) {
if (annotation == null || annotation.getAnnotation() == null) {
return false;
}
Element accountType = SchemaProcessorUtil.getAnnotationElement(annotation, MidPointConstants.RA_ACCOUNT);
if (accountType != null) {
return true;
}
return false;
}
private ResourceAttributeDefinition getAnnotationReference(XSAnnotation annotation, QName qname, ObjectClassComplexTypeDefinition objectClass) throws SchemaException {
Element element = SchemaProcessorUtil.getAnnotationElement(annotation, qname);
if (element != null) {
String reference = element.getTextContent();
if (reference == null || reference.isEmpty()) {
// Compatibility
reference = element.getAttribute("ref");
}
if (reference != null && !reference.isEmpty()) {
QName referenceItemName = DOMUtil.resolveQName(element, reference);
PrismPropertyDefinition definition = objectClass.findPropertyDefinition(referenceItemName);
if (definition == null) {
throw new SchemaException("The annotation "+qname+" in "+objectClass+" is pointing to "+referenceItemName+" which does not exist");
}
if (definition instanceof ResourceAttributeDefinition) {
return (ResourceAttributeDefinition) definition;
} else {
throw new SchemaException("The annotation "+qname+" in "+objectClass+" is pointing to "+referenceItemName+" which is not an attribute, it is "+definition);
}
}
}
return null;
}
}