@Override public Object invokeProjection( final String resolvedXpath, final Object proxy, final Object[] args) throws Throwable { // final String pathToElement = resolvedXpath.replaceAll("\\[@", // "[attribute::").replaceAll("/?@.*", "").replaceAll("\\[attribute::", "[@"); lastInvocationContext.updateMethodArgs(args); final Document document = DOMHelper.getOwnerDocumentFor(node); assert document != null; final Object valueToSet = args[findIndexOfValue]; final boolean isMultiValue = isMultiValue(method.getParameterTypes()[findIndexOfValue]); // ROOT element update if ("/*".equals(resolvedXpath)) { // Setting a new root element. if (isMultiValue) { throw new IllegalArgumentException( "Method " + method + " was invoked as setter changing the document root element, but tries to set multiple values."); } return handeRootElementReplacement(proxy, method, document, valueToSet); } final boolean wildCardTarget = resolvedXpath.endsWith("/*"); try { if (!lastInvocationContext.isStillValid(resolvedXpath)) { final DuplexExpression duplexExpression = wildCardTarget ? new DuplexXPathParser(projector.config().getUserDefinedNamespaceMapping()) .compile(resolvedXpath.substring(0, resolvedXpath.length() - 2)) : new DuplexXPathParser(projector.config().getUserDefinedNamespaceMapping()) .compile(resolvedXpath); MethodParamVariableResolver resolver = null; if (duplexExpression.isUsingVariables()) { resolver = new MethodParamVariableResolver( method, args, duplexExpression, projector.config().getStringRenderer(), null); duplexExpression.setXPathVariableResolver(resolver); } Class<?> targetComponentType = findTargetComponentType(method); lastInvocationContext = new InvocationContext( resolvedXpath, null, null, duplexExpression, resolver, targetComponentType, projector); } final DuplexExpression duplexExpression = lastInvocationContext.getDuplexExpression(); if (duplexExpression.getExpressionType().isMustEvalAsString()) { throw new XBPathException("Unwriteable xpath selector used ", method, resolvedXpath); } // MULTIVALUE if (isMultiValue) { if (duplexExpression.getExpressionType().equals(ExpressionType.ATTRIBUTE)) { throw new IllegalArgumentException( "Method " + method + " was invoked as setter changing some attribute, but was declared to set multiple values. I can not create multiple attributes for one path."); } final Iterable<?> iterable2Set = valueToSet == null ? Collections.emptyList() : (valueToSet.getClass().isArray()) ? ReflectionHelper.array2ObjectList(valueToSet) : (Iterable<?>) valueToSet; if (wildCardTarget) { // TODO: check support of ParameterizedType e.g. Supplier final Element parentElement = (Element) duplexExpression.ensureExistence(node); DOMHelper.removeAllChildren(parentElement); int count = 0; for (Object o : iterable2Set) { if (o == null) { continue; } ++count; if (o instanceof Node) { DOMHelper.appendClone(parentElement, (Node) o); continue; } if (o instanceof DOMAccess) { DOMHelper.appendClone(parentElement, ((DOMAccess) o).getDOMBaseElement()); continue; } throw new XBPathException( "When using a wildcard target, the type to set must be a DOM Node or another projection. Otherwise I can not determine the element name.", method, resolvedXpath); } return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(count)); } final Element parentElement = duplexExpression.ensureParentExistence(node); duplexExpression.deleteAllMatchingChildren(parentElement); int count = applyIterableSetOnElement(iterable2Set, parentElement, duplexExpression); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(count)); } // ATTRIBUTES if (duplexExpression.getExpressionType().equals(ExpressionType.ATTRIBUTE)) { if (wildCardTarget) { // TODO: This may never happen, right? throw new XBPathException( "Wildcards are not allowed when writing to an attribute. I need to know to which Element I should set the attribute", method, resolvedXpath); } Attr attribute = (Attr) duplexExpression.ensureExistence(node); if (valueToSet == null) { attribute.getOwnerElement().removeAttributeNode(attribute); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } DOMHelper.setStringValue(attribute, valueToSet.toString()); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } if ((valueToSet instanceof Node) || (valueToSet instanceof DOMAccess)) { if (valueToSet instanceof Attr) { if (wildCardTarget) { throw new XBPathException( "Wildcards are not allowed when writing an attribute. I need to know to which Element I should set the attribute", method, resolvedXpath); } Element parentNode = duplexExpression.ensureParentExistence(node); if (((Attr) valueToSet).getNamespaceURI() != null) { parentNode.setAttributeNodeNS((Attr) valueToSet); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } parentNode.setAttributeNode((Attr) valueToSet); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } final Element newNodeOrigin = valueToSet instanceof DOMAccess ? ((DOMAccess) valueToSet).getDOMBaseElement() : (Element) valueToSet; final Element newNode = (Element) newNodeOrigin.cloneNode(true); DOMHelper.ensureOwnership(document, newNode); if (wildCardTarget) { Element parentElement = (Element) duplexExpression.ensureExistence(node); DOMHelper.removeAllChildren(parentElement); parentElement.appendChild(newNode); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } Element previousElement = (Element) duplexExpression.ensureExistence(node); DOMHelper.replaceElement(previousElement, newNode); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } final Element elementToChange = (Element) duplexExpression.ensureExistence(node); if (valueToSet == null) { // TODO: This should depend on the parameter type? // If param type == String, no structural change might be expected. DOMHelper.removeAllChildren(elementToChange); } else { final String asString = projector .config() .getStringRenderer() .render( valueToSet.getClass(), valueToSet, duplexExpression.getExpressionFormatPattern()); elementToChange.setTextContent(asString); } return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } catch (XBPathParsingException e) { throw new XBPathException(e, method, resolvedXpath); } }