private static void visit(@NonNull XmlElement xmlElement) { for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) { Matcher matcher = PlaceholderHandler.PATTERN.matcher(xmlAttribute.getValue()); if (matcher.matches()) { String encodedValue = "dollar_openBracket_" + matcher.group(2) + "_closeBracket"; xmlAttribute.getXml().setValue(encodedValue); } } for (XmlElement childElement : xmlElement.getMergeableElements()) { visit(childElement); } } }
/** * Enforces {@link com.android.SdkConstants#ANDROID_URI} declaration in the top level element. * It is possible that the original manifest file did not contain any attribute declaration, * therefore not requiring a xmlns: declaration. Yet the implicit elements handling may have * added attributes requiring the namespace declaration. */ private static void enforceAndroidNamespaceDeclaration(@NonNull XmlDocument xmlDocument) { XmlElement manifest = xmlDocument.getRootNode(); for (XmlAttribute xmlAttribute : manifest.getAttributes()) { if (xmlAttribute.getXml().getName().startsWith(SdkConstants.XMLNS) && SdkConstants.ANDROID_URI.equals(xmlAttribute.getValue())) { return; } } // if we are here, we did not find the namespace declaration, add it. manifest.getXml().setAttribute(SdkConstants.XMLNS + ":" + "android", SdkConstants.ANDROID_URI); }
/** * shorten recursively all attributes that are package dependent of the passed nodes and all * its child nodes. * @param packageName the manifest package name. * @param xmlElement the xml element to process recursively. */ private static void extractFcqns(@NonNull String packageName, @NonNull XmlElement xmlElement) { for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) { if (xmlAttribute.getModel() !=null && xmlAttribute.getModel().isPackageDependent()) { String value = xmlAttribute.getValue(); if (value.startsWith(packageName) && value.charAt(packageName.length()) == '.') { xmlAttribute.getXml().setValue(value.substring(packageName.length())); } } } for (XmlElement child : xmlElement.getMergeableElements()) { extractFcqns(packageName, child); } }
private static Optional<String> checkAttributes( @NonNull XmlElement expected, @NonNull XmlElement actual) { for (XmlAttribute expectedAttr : expected.getAttributes()) { XmlAttribute.NodeName attributeName = expectedAttr.getName(); if (attributeName.isInNamespace(SdkConstants.TOOLS_URI)) { continue; } Optional<XmlAttribute> actualAttr = actual.getAttribute(attributeName); if (actualAttr.isPresent()) { if (!expectedAttr.getValue().equals(actualAttr.get().getValue())) { return Optional.of( String.format("Attribute %1$s do not match: %2$s versus %3$s at %4$s", expectedAttr.getId(), expectedAttr.getValue(), actualAttr.get().getValue(), actual.printPosition())); } } else { return Optional.of(String.format("Attribute %1$s not found at %2$s", expectedAttr.getId(), actual.printPosition())); } } return Optional.absent(); }
@NonNull MergingReport.Builder mergingReportBuilder) { for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) {
private void parse(@NonNull XmlElement element, @NonNull ImmutableMultimap.Builder<Integer, Record> mappings) { DecisionTreeRecord decisionTreeRecord = mRecords.get(element.getId()); if (decisionTreeRecord != null) { Actions.NodeRecord nodeRecord = findNodeRecord(decisionTreeRecord); if (nodeRecord != null) { mappings.put(element.getPosition().getStartLine(), nodeRecord); } for (XmlAttribute xmlAttribute : element.getAttributes()) { Actions.AttributeRecord attributeRecord = findAttributeRecord(decisionTreeRecord, xmlAttribute); if (attributeRecord != null) { mappings.put(xmlAttribute.getPosition().getStartLine(), attributeRecord); } } } for (XmlElement xmlElement : element.getMergeableElements()) { parse(xmlElement, mappings); } }
/** * When the first xml file is loaded, there is nothing to merge with, however, each xml element * and attribute added to the initial merged file need to be recorded. * * @param xmlElement xml element added to the initial merged document. */ synchronized void recordDefaultNodeAction(@NonNull XmlElement xmlElement) { if (!mRecords.containsKey(xmlElement.getOriginalId())) { recordNodeAction(xmlElement, Actions.ActionType.ADDED); for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) { AttributeOperationType attributeOperation = xmlElement .getAttributeOperationType(xmlAttribute.getName()); recordAttributeAction( xmlAttribute, Actions.ActionType.ADDED, attributeOperation); } for (XmlElement childNode : xmlElement.getMergeableElements()) { recordDefaultNodeAction(childNode); } } }
/** * Validate attributes part of the {@link com.android.SdkConstants#ANDROID_URI} * @param mergingReport report to log warnings and errors. * @param xmlElement xml element to check its attributes. */ private static void validateAndroidAttributes(@NonNull MergingReport.Builder mergingReport, @NonNull XmlElement xmlElement) { for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) { AttributeModel model = xmlAttribute.getModel(); if (model != null && model.getOnReadValidator() != null) { model.getOnReadValidator().validates( mergingReport, xmlAttribute, xmlAttribute.getValue()); } } }
/** * Validates all {@link com.android.manifmerger.XmlElement} attributes belonging to the * {@link com.android.SdkConstants#ANDROID_URI} namespace. * * @param xmlElement xml element to check the attributes from. * @param mergingReport report for errors and warnings. */ private static void validateAndroidAttributes(@NonNull XmlElement xmlElement, @NonNull MergingReport.Builder mergingReport) { for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) { if (xmlAttribute.getModel() != null) { AttributeModel.Validator onWriteValidator = xmlAttribute.getModel() .getOnWriteValidator(); if (onWriteValidator != null) { onWriteValidator.validates( mergingReport, xmlAttribute, xmlAttribute.getValue()); } } } } }
for (XmlAttribute lowerPriorityAttribute : lowerPriorityNode.getAttributes()) { lowerPriorityAttribute.mergeInHigherPriorityElement(this, mergingReport); if (lowerPriorityAttribute.getModel() != null) {