public inherited sharing class MetadataXml { public static String escape(String value) { if (value == null) { return ''; } return value .replace('&', '&') .replace('<', '<') .replace('>', '>') .replace('"', '"') .replace('\'', '''); } public static Dom.XmlNode child(Dom.XmlNode parent, String localName) { if (parent == null) { return null; } for (Dom.XmlNode candidate : parent.getChildElements()) { if (candidate.getName() == localName) { return candidate; } } return null; } public static List children( Dom.XmlNode parent, String localName ) { List matches = new List(); if (parent == null) { return matches; } for (Dom.XmlNode candidate : parent.getChildElements()) { if (candidate.getName() == localName) { matches.add(candidate); } } return matches; } public static Dom.XmlNode findDescendant( Dom.XmlNode parent, String localName ) { if (parent == null) { return null; } if (parent.getName() == localName) { return parent; } for (Dom.XmlNode candidate : parent.getChildElements()) { Dom.XmlNode match = findDescendant(candidate, localName); if (match != null) { return match; } } return null; } public static String text(Dom.XmlNode parent, String localName) { Dom.XmlNode valueNode = child(parent, localName); return valueNode == null ? null : valueNode.getText(); } public static Boolean booleanValue(Dom.XmlNode parent, String localName) { return 'true'.equalsIgnoreCase(text(parent, localName)); } public static String serialize(Dom.XmlNode node) { String name = node.getName(); List childNodes = node.getChildElements(); if (childNodes.isEmpty()) { return '<' + name + '>' + escape(node.getText()) + ''; } String result = '<' + name + '>'; for (Dom.XmlNode childNode : childNodes) { result += serialize(childNode); } return result + ''; } public static String serializeChildren(Dom.XmlNode parent) { String result = ''; for (Dom.XmlNode childNode : parent.getChildElements()) { result += serialize(childNode); } return result; } public static String replaceOrInsertChild( Dom.XmlNode parent, String childName, String replacementXml ) { String result = ''; Boolean replaced = false; for (Dom.XmlNode childNode : parent.getChildElements()) { if (childNode.getName() == childName) { if (!replaced) { result += replacementXml; replaced = true; } } else if (!replaced && childNode.getName().compareTo(childName) > 0) { result += replacementXml; result += serialize(childNode); replaced = true; } else { result += serialize(childNode); } } if (!replaced) { result += replacementXml; } return result; } public static String developerName(String label) { String value = String.isBlank(label) ? '' : label.trim().replaceAll('[^A-Za-z0-9_]+', '_'); value = value.replaceAll('_+', '_').replaceAll('^_+|_+$', ''); if (String.isBlank(value)) { throw new IllegalArgumentException( 'A developer name could not be generated from the label.' ); } if (Pattern.matches('^[0-9].*', value)) { value = 'X_' + value; } return value.length() > 80 ? value.substring(0, 80) : value; } public static void require(Boolean condition, String message) { if (!condition) { throw new IllegalArgumentException(message); } } }