private Object handeRootElementReplacement( final Object proxy, final Method method, final Document document, final Object valueToSet) { int count = document.getDocumentElement() == null ? 0 : 1; if (valueToSet == null) { DOMHelper.setDocumentElement(document, null); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(count)); } if (valueToSet instanceof Element) { Element clone = (Element) ((Element) valueToSet).cloneNode(true); document.adoptNode(clone); if (document.getDocumentElement() == null) { document.appendChild(clone); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } document.replaceChild(document.getDocumentElement(), clone); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(1)); } if (!(valueToSet instanceof DOMAccess)) { throw new IllegalArgumentException( "Method " + method + " was invoked as setter changing the document root element. Expected value type was a projection so I can determine a element name. But you provided a " + valueToSet); } DOMAccess projection = (DOMAccess) valueToSet; Element element = projection.getDOMBaseElement(); assert element != null; DOMHelper.setDocumentElement(document, element); return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(count)); }
/** * @param typeToSet * @param iterable * @param parentElement * @param duplexExpression * @param elementSelector */ private int applyIterableSetOnElement( final Iterable<?> iterable, final Element parentElement, final DuplexExpression duplexExpression) { int changeCount = 0; for (Object o : iterable) { if (o == null) { continue; } if (!isStructureChangingValue(o)) { final Node newElement = duplexExpression.createChildWithPredicate(parentElement); final String asString = projector .config() .getStringRenderer() .render(o.getClass(), o, duplexExpression.getExpressionFormatPattern()); newElement.setTextContent(asString); ++changeCount; continue; } Element elementToAdd; if (o instanceof Node) { final Node n = (Node) o; elementToAdd = (Element) (Node.DOCUMENT_NODE != n.getNodeType() ? n : n.getOwnerDocument() == null ? null : n.getOwnerDocument().getDocumentElement()); } else { final DOMAccess p = (DOMAccess) o; elementToAdd = p.getDOMBaseElement(); } if (elementToAdd == null) { continue; } Element clone = (Element) elementToAdd.cloneNode(true); Element childWithPredicate = (Element) duplexExpression.createChildWithPredicate(parentElement); final String elementName = childWithPredicate.getNodeName(); if (!elementName.equals(clone.getNodeName())) { if (!"*".equals(elementName)) { clone = DOMHelper.renameElement(clone, elementName); } } DOMHelper.replaceElement(childWithPredicate, clone); ++changeCount; } return changeCount; }
@Override public Object invokeXpathProjection( final InvocationContext invocationContext, final Object proxy, final Object[] args) throws Throwable { assert ReflectionHelper.hasParameters(method); final Node node = getNodeForMethod(method, args); // final Document document = DOMHelper.getOwnerDocumentFor(node); // final XPath xPath = projector.config().createXPath(document); final XPathExpression expression = invocationContext.getxPathExpression(); final Object valueToSet = args[findIndexOfValue]; // final Class<?> typeToSet = method.getParameterTypes()[findIndexOfValue]; // final boolean isMultiValue = isMultiValue(typeToSet); NodeList nodes = (NodeList) expression.evaluate(node, XPathConstants.NODESET); final int count = nodes.getLength(); for (int i = 0; i < count; ++i) { final Node n = nodes.item(i); if (n == null) { continue; } if (Node.ATTRIBUTE_NODE == n.getNodeType()) { Element e = ((Attr) n).getOwnerElement(); if (e == null) { continue; } DOMHelper.setOrRemoveAttribute( e, n.getNodeName(), valueToSet == null ? null : valueToSet.toString()); continue; } if (valueToSet instanceof Element) { if (!(n instanceof Element)) { throw new IllegalArgumentException( "XPath for element update need to select elements only"); } DOMHelper.replaceElement((Element) n, (Element) ((Element) valueToSet).cloneNode(true)); continue; } n.setTextContent(valueToSet == null ? null : valueToSet.toString()); } return getProxyReturnValueForMethod(proxy, method, Integer.valueOf(count)); }
@Override protected final Object invokeProjection( final String resolvedXpath, final Object proxy, final Object[] args) throws Throwable { final XPath xPath = projector.config().createXPath(DOMHelper.getOwnerDocumentFor(node)); if (!lastInvocationContext.isStillValid(resolvedXpath)) { final DuplexExpression duplexExpression = new DuplexXPathParser(projector.config().getUserDefinedNamespaceMapping()) .compile(resolvedXpath); String strippedXPath = duplexExpression.getExpressionAsStringWithoutFormatPatterns(); MethodParamVariableResolver resolver = null; if (duplexExpression.isUsingVariables()) { XPathVariableResolver peviousResolver = xPath.getXPathVariableResolver(); resolver = new MethodParamVariableResolver( method, args, duplexExpression, projector.config().getStringRenderer(), peviousResolver); xPath.setXPathVariableResolver(resolver); } final XPathExpression xPathExpression = xPath.compile(strippedXPath); final Class<?> targetComponentType = findTargetComponentType(method); lastInvocationContext = new InvocationContext( resolvedXpath, xPath, xPathExpression, duplexExpression, resolver, targetComponentType, projector); } lastInvocationContext.updateMethodArgs(args); return invokeXpathProjection(lastInvocationContext, proxy, args); }
@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); } }