/** * Adds all children of an Iterable to this parent. * * @param children The children. */ public default void addChildren(Iterable<? extends Child> children) { for (final Child child : children) { addChild(child); } }
/** * Adds a comment as last child. * <p> * If last child exists and is a comment, {@code content} is added to this last child.<br> * Otherwise, a comment child is created and added to last position. * * @param content The comment content. * @return The modified or created comment. */ public default Comment addComment(String content) { return addComment(content, true); }
@Override public final void setParent(Parent parent) { if (parent == this.parent) { // Ignore } else if (parent == null || parent.canAddChild(this)) { // We always allow setting a null parent // Detach current parent if (this.parent != null) { this.parent.getModifiableChildren().remove(this); this.parent = null; } // Attach new parent if (parent != null) { parent.getModifiableChildren().add(this); this.parent = parent; } } else { throw new IllegalStateException("Can not set parent. child: " + getType() + " parent: " + parent.getType()); } }
/** * Removes all children locally. */ public default void removeChildren() { while (hasChildren()) { removeChildAt(getChildrenCount() - 1); } }
/** * Sorts children locally. * * @param comparator The comparator. */ public default void sortChildren(Comparator<? super Child> comparator) { if (hasChildren()) { Collections.sort(getChildren(), comparator); } }
/** * Changes all texts recursively. * * @param modifier A function that take content and returns new content. * @param recurse If {@code true}, then this is applied recursively. */ public default void changeTexts(Function<String, String> modifier, boolean recurse) { changeTexts(modifier); if (recurse) { for (final Element child : getElements()) { child.changeTexts(modifier, true); } } }
/** * @return An Iterable of children elements. */ public default Iterable<Element> getElements() { return getChildren(Element.class); }
/** * Removes all children that match a predicate. * <p> * This does only a shallow removal.<br> * For deep removal, use {@link cdc.util.data.util.DataUtil}. * * @param predicate The predicate. */ public default void removeChildren(Predicate<? super Child> predicate) { List<Integer> indices = null; // Add candidates to removal in reverse order // Better performances for removal for (int index = getChildrenCount() - 1; index >= 0; index--) { final Child child = getChildAt(index); if (predicate.test(child)) { if (indices == null) { indices = new ArrayList<>(); } indices.add(index); } } if (indices != null) { for (final int index : indices) { final Child removed = removeChildAt(index); assert removed != null; } } }
/** * Merges all consecutive texts locally. */ public default void mergeTexts() { if (getChildrenCount() > 1) { Child ref = getChildAt(0); int index = 1; while (index < getChildrenCount()) { final Child next = getChildAt(index); if (ref.getType() == NodeType.TEXT && next.getType() == NodeType.TEXT) { // ref and next are both texts: merge them ((Text) ref).appendContent(((Text) next).getContent()); // Remove next next.detach(); // Do not change index and ref } else { ref = next; index++; } } } }
/** * Changes or set text under element that have a given name, recursively. * * @param name The element name. * @param modifier A function that take content and returns new content. * @param recurse If {@code true}, then this is applied recursively. */ public default void changeNamedTexts(String name, Function<String, String> modifier, boolean recurse) { changeNamedTexts(name, modifier); if (recurse) { for (final Element child : getElements()) { child.changeNamedTexts(name, modifier, true); } } }
/** * Changes all comments recursively. * * @param modifier A function that take content and returns new content. * @param recurse If {@code true}, then this is applied recursively. */ public default void changeComments(Function<String, String> modifier, boolean recurse) { changeComments(modifier); if (recurse) { for (final Element child : getElements()) { child.changeComments(modifier, true); } } } }
/** * Removes the child located at a given index. * <p> * If index is invalid, returns {@code false}. * * @param index The index (0-based). * @return The removed child. * @throws IndexOutOfBoundsException When index is out of range ({@code index < 0 || index >= getChildrenCount()}). */ public default Child removeChildAt(int index) { final AbstractChild child = (AbstractChild) getChildren().get(index); getModifiableChildren().remove(child); child.resetParent(); return child; }
/** * Sorts children nodes using a user-defined comparator. * <p> * Uses a Depth First traversal for recursion.<br> * Sort is applied on parent before its children. * * @param parent The initial node. <em>MUST NOT</em> be null. * @param predicate The predicate of nodes whose children must be sorted. <em>MUST NOT</em> be null. * @param comparator The children comparator. * @param evaluator The evaluator. <em>MUST NOT</em> be null. */ public static void sortChildren(Parent parent, Predicate<? super Parent> predicate, Comparator<? super Child> comparator, Evaluator<? super Parent> evaluator) { if (predicate.test(parent) && parent.getChildren() != null) { parent.sortChildren(comparator); for (final Parent child : parent.getChildren(Parent.class)) { if (evaluator.evaluate(child) == Evaluation.CONTINUE) { sortChildren(child, predicate, comparator, evaluator); } } } }
/** * Removes matching children. * <p> * Uses a Depth First traversal for recursion.<br> * Removal is applied on children before parent. * * @param parent The initial node. <em>MUST NOT</em> be null. * @param predicate The predicate of children nodes to remove. <em>MUST NOT</em> be null. * @param evaluator The evaluator. <em>MUST NOT</em> be null. */ public static void removeMatchingChildren(Parent parent, Predicate<? super Child> predicate, Evaluator<? super Parent> evaluator) { // First, recursive processing for (final Parent child : parent.getChildren(Parent.class)) { if (evaluator.evaluate(child) == Evaluation.CONTINUE) { removeMatchingChildren(child, predicate, evaluator); } } // Then local removal process parent.removeChildren(predicate); }
/** * Removes active text if possible and necessary. * * @param preserve If {@code true} should be preserved if necessary. * * @throws SAXException When mixed content is found and is not allowed. */ private void checkActiveText(boolean preserve) throws SAXException { if (activeText != null) { if (preserve && currentParent.getChildrenCount() == 1) { // Preserve the text that is the only child of its parent (element) resetActiveText(); } else if (activeText.getContent().isEmpty() || activeText.isIgnorable() && !isEnabled(Feature.LOAD_SPACES)) { currentParent.removeChildAt(currentParent.getChildrenCount() - 1); resetActiveText(); } else if (currentParent.getChildrenCount() > 1 && !isEnabled(Feature.ALLOW_MIXED_CONTENT)) { throw new SAXException("Mixed content not allowed"); } } }
/** * Adds a comment as last child. * * @param content The comment content. * @param merge If {@code true}, then if last child exists and is a comment, * {@code content} is added to this last child.<br> * Otherwise, a comment child is created and added to last position. * @return The modified or created comment. */ public default Comment addComment(String content, boolean merge) { if (merge) { final Child last = getLastChild(); if (last != null && last.getType() == NodeType.COMMENT) { ((Comment) last).appendContent(content); return (Comment) last; } } final Comment result = new Comment(content); addChild(result); return result; }
if (top() == Action.KEEP) { checkActiveText(false); if (!isEnabled(Feature.ALLOW_MIXED_CONTENT) && currentParent.hasChildren(Text.class)) { throw new SAXException("Mixed content not allowed"); currentParent.addChild(element); currentParent = element; resetActiveText();
public default Element getElementNamedWithAttribute(String name, String attributeName, String attributeValue) { return getChild(Element.class, Element.namedWithAttribute(name, attributeName, attributeValue)); }
/** * @return An Iterable if text children. */ public default Iterable<Text> getTexts() { return getChildren(Text.class); }
/** * Merges all consecutive comments locally. */ public default void mergeComments() { if (getChildrenCount() > 1) { Child ref = getChildAt(0); int index = 1; while (index < getChildrenCount()) { final Child next = getChildAt(index); if (ref.getType() == NodeType.COMMENT && next.getType() == NodeType.COMMENT) { // ref and next are both texts: merge them ((Comment) ref).appendContent(((Comment) next).getContent()); // Remove next next.detach(); // Do not change index and ref } else { ref = next; index++; } } } }