private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
for (Field next : theClass.getDeclaredFields()) {
Child childAnnotation = next.getAnnotation(Child.class);
if (childAnnotation == null) {
ourLog.debug("Ignoring non-type field '" + next.getName() + "' on target type: " + theClass);
continue;
}
Description descriptionAnnotation = next.getAnnotation(Description.class);
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
Extension extensionAttr = next.getAnnotation(Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
String elementName = childAnnotation.name();
int order = childAnnotation.order();
if (order == Child.REPLACE_PARENT) {
if (extensionAttr != null) {
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
if (nextDef instanceof RuntimeChildDeclaredExtensionDefinition) {
if (nextDef.getExtensionUrl().equals(extensionAttr.url())) {
order = nextEntry.getKey();
orderMap.remove(nextEntry.getKey());
elementNames.remove(elementName);
break;
}
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with extension URL " + extensionAttr.url()
+ " could be found on type " + next.getDeclaringClass().getSimpleName());
}
} else {
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
if (elementName.equals(nextDef.getElementName())) {
order = nextEntry.getKey();
orderMap.remove(nextEntry.getKey());
elementNames.remove(elementName);
break;
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with name " + elementName + " could be found on type "
+ next.getDeclaringClass().getSimpleName());
}
}
}
if (order < 0 && order != Child.ORDER_UNKNOWN) {
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + next.getName() + "' on target type: " + theClass);
}
if (order != Child.ORDER_UNKNOWN) {
order = order + baseElementOrder;
}
int min = childAnnotation.min();
int max = childAnnotation.max();
/*
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict wityh any
* given IDs and can be figured out later
*/
while (order == Child.ORDER_UNKNOWN && orderMap.containsKey(order)) {
order--;
}
List<Class<? extends IElement>> choiceTypes = new ArrayList<Class<? extends IElement>>();
for (Class<? extends IElement> nextChoiceType : childAnnotation.type()) {
choiceTypes.add(nextChoiceType);
}
if (orderMap.containsKey(order)) {
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
}
if (elementNames.contains(elementName)) {
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
}
Class<?> nextElementType = determineElementType(next);
if (nextElementType.equals(ContainedDt.class)) {
/*
* Child is contained resources
*/
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
orderMap.put(order, def);
} else if (choiceTypes.size() > 1 && !ResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
/*
* Child is a choice element
*/
for (Class<? extends IElement> nextType : choiceTypes) {
addScanAlso(nextType);
}
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
orderMap.put(order, def);
} else if (next.getType().equals(ExtensionDt.class)) {
RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
if (IElement.class.isAssignableFrom(nextElementType)) {
addScanAlso((Class<? extends IElement>) nextElementType);
}
} else if (extensionAttr != null) {
/*
* Child is an extension
*/
Class<? extends IElement> et = (Class<? extends IElement>) nextElementType;
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et);
orderMap.put(order, def);
if (IElement.class.isAssignableFrom(nextElementType)) {
addScanAlso((Class<? extends IElement>) nextElementType);
}
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
/*
* Child is a resource reference
*/
List<Class<? extends IResource>> refTypesList = new ArrayList<Class<? extends IResource>>();
for (Class<? extends IElement> nextType : childAnnotation.type()) {
if (IResource.class.isAssignableFrom(nextType) == false) {
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + ResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
}
refTypesList.add((Class<? extends IResource>) nextType);
addScanAlso(nextType);