void (@NotNull Element element, boolean caseSensitive, boolean recursively, @Nullable PathMacroFilter filter) { if (filter != null && filter.skipPathMacros(element)) { return; } for (Content child : element.getContent()) { if (child instanceof Element) { substitute((Element)child, caseSensitive, recursively, filter); } else if (child instanceof Text) { Text t = (Text)child; String oldText = t.getText(); String newText = recursively ? substituteRecursively(oldText, caseSensitive).toString() : substitute(oldText, caseSensitive); if (!Strings.areSameInstance(oldText, newText)) { // it is faster to call 'setText' right away than perform additional 'equals' check t.setText(newText); } } else { LOG.error("Wrong content: " + child.getClass()); } } if (!element.hasAttributes()) { return; } for (Attribute attribute : element.getAttributes()) { if (filter == null || !filter.skipPathMacros(attribute)) { String newValue = getAttributeValue(attribute, filter, caseSensitive, recursively); if (!Strings.areSameInstance(attribute.getValue(), newValue)) { // it is faster to call 'setValue' right away than perform additional 'equals' check attribute.setValue(newValue); } } } }