/** * Returns a display friendly identification string that can be used in machine and user * readable messages. */ @NonNull @Override public NodeKey getId() { // (Id of the parent element)@(my name) String myName = mXml.getNamespaceURI() == null ? mXml.getName() : mXml.getLocalName(); return new NodeKey(mOwnerElement.getId() + "@" + myName); }
/** * Validate an xml declaration with 'tools:node="removeAll" annotation. There should not * be any other attribute declaration on this element. */ private static void validateRemoveAllOperation(@NonNull MergingReport.Builder mergingReport, @NonNull XmlElement element) { NamedNodeMap attributes = element.getXml().getAttributes(); if (attributes.getLength() > 1) { List<String> extraAttributeNames = new ArrayList<String>(); for (int i = 0; i < attributes.getLength(); i++) { Node item = attributes.item(i); if (!(SdkConstants.TOOLS_URI.equals(item.getNamespaceURI()) && NodeOperationType.NODE_LOCAL_NAME.equals(item.getLocalName()))) { extraAttributeNames.add(item.getNodeName()); } } String message = String.format( "Element %1$s at %2$s annotated with 'tools:node=\"removeAll\"' cannot " + "have other attributes : %3$s", element.getId(), element.printPosition(), Joiner.on(',').join(extraAttributeNames) ); element.addMessage(mergingReport, ERROR, message); } }
"Missing one of the key attributes '%1$s' on element %2$s at %3$s", Joiner.on(',').join(keyAttributesNames), xmlElement.getId(), xmlElement.printPosition()) : String.format( "Missing '%1$s' key attribute on element %2$s at %3$s", keyAttributesNames.get(0), xmlElement.getId(), xmlElement.printPosition()); xmlElement.addMessage(mergingReport, ERROR, message);
private static void checkSelectorPresence(@NonNull MergingReport.Builder mergingReport, @NonNull XmlElement element) { Attr selectorAttribute = element.getXml().getAttributeNodeNS(SdkConstants.TOOLS_URI, Selector.SELECTOR_LOCAL_NAME); if (selectorAttribute!=null && !element.supportsSelector()) { String message = String.format( "Unsupported tools:selector=\"%1$s\" found on node %2$s at %3$s", selectorAttribute.getValue(), element.getId(), element.printPosition()); element.addMessage(mergingReport, ERROR, message); } }
/** * Check in our list of applied actions that a particular * {@link com.android.manifmerger.Actions.ActionType} action was recorded on the passed element. * @return true if it was applied, false otherwise. */ private static boolean isNodeOperationPresent(@NonNull XmlElement xmlElement, @NonNull Actions actions, ActionType action) { for (Actions.NodeRecord nodeRecord : actions.getNodeRecords(xmlElement.getId())) { if (nodeRecord.getActionType() == action) { return true; } } return false; }
/** * Check in our list of attribute actions that a particular * {@link com.android.manifmerger.Actions.ActionType} action was recorded on the passed element. * @return true if it was applied, false otherwise. */ private static boolean isAttributeOperationPresent(@NonNull XmlElement xmlElement, @NonNull Map.Entry<XmlNode.NodeName, AttributeOperationType> attributeOperation, @NonNull Actions actions, ActionType action) { for (Actions.AttributeRecord attributeRecord : actions.getAttributeRecords( xmlElement.getId(), attributeOperation.getKey())) { if (attributeRecord.getActionType() == action) { return true; } } return false; }
"%1$s: Number of children do not match up: " + "expected %2$d versus %3$d at %4$s, missing %5$s", getId(), expectedChildren.size(), actualChildren.size(), "%1$s: Number of children do not match up: " + "expected %2$d versus %3$d at %4$s, extra elements found : %5$s", getId(), expectedChildren.size(), actualChildren.size(),
} else { if (checkKeyPresence(mergingReport, childElement)) { XmlElement twin = childrenKeys.get(childElement.getId()); if (twin != null && !childElement.getType().areMultipleDeclarationAllowed()) { childElement.getId(), childElement.printPosition(), childrenKeys.get(childElement.getId()).printPosition()); if (twin.compareTo(childElement).isPresent()) { childElement.addMessage(mergingReport, ERROR, message); childrenKeys.put(childElement.getId(), childElement);
@NonNull private static XmlElement createOrGetElement( @NonNull ActionRecorder actionRecorder, @NonNull XmlDocument document, @NonNull ManifestModel.NodeTypes nodeType, @NonNull String message) { Element manifest = document.getXml().getDocumentElement(); NodeList nodes = manifest.getElementsByTagName(nodeType.toXmlName()); if (nodes.getLength() == 0) { nodes = manifest.getElementsByTagNameNS( SdkConstants.ANDROID_URI, nodeType.toXmlName()); } if (nodes.getLength() == 0) { // create it first. Element node = manifest.getOwnerDocument().createElement(nodeType.toXmlName()); manifest.appendChild(node); XmlElement xmlElement = new XmlElement(node, document); Actions.NodeRecord nodeRecord = new Actions.NodeRecord( Actions.ActionType.INJECTED, new SourceFilePosition(xmlElement.getSourceFile(), SourcePosition.UNKNOWN), xmlElement.getId(), message, NodeOperationType.STRICT); actionRecorder.recordNodeAction(xmlElement, nodeRecord); return xmlElement; } else { return new XmlElement((Element) nodes.item(0), document); } }
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); } }
/** * Merges two children when this children's type allow multiple elements declaration with the * same key value. In that case, we only merge the lower priority child if there is not already * an element with the same key value that is equal to the lower priority child. Two children * are equals if they have the same attributes and children declared irrespective of the * declaration order. * * @param lowerPriorityChild the lower priority element's child. * @param mergingReport the merging report to log errors and actions. */ private void mergeChildrenWithMultipleDeclarations( @NonNull XmlElement lowerPriorityChild, @NonNull MergingReport.Builder mergingReport) { Preconditions.checkArgument(lowerPriorityChild.getType().areMultipleDeclarationAllowed()); if (lowerPriorityChild.getType().areMultipleDeclarationAllowed()) { for (XmlElement sameTypeChild : getAllNodesByType(lowerPriorityChild.getType())) { if (sameTypeChild.getId().equals(lowerPriorityChild.getId()) && sameTypeChild.isEquals(lowerPriorityChild)) { return; } } } // if we end up here, we never found a child of this element with the same key and strictly // equals to the lowerPriorityChild so we should merge it in. addElement(lowerPriorityChild, mergingReport); }
? message : Optional.of(String.format("Child %1$s not found in document %2$s", childNode.getId(), otherElement.printPosition()));
"%1$s@%2$s was tagged at %3$s:%4$d to remove other" + " declarations but no other declaration present", xmlElement.getId(), attributeOperation.getKey(), xmlElement.getDocument().getSourceFile().print(true), "%1$s@%2$s was tagged at %3$s:%4$d to replace other" + " declarations but no other declaration present", xmlElement.getId(), attributeOperation.getKey(), xmlElement.getDocument().getSourceFile().print(true),
"%1$s was tagged at %2$s:%3$d to replace another declaration " + "but no other declaration present", xmlElement.getId(), xmlElement.getDocument().getSourceFile().print(true), xmlElement.getPosition().getStartLine() + 1 "%1$s was tagged at %2$s:%3$d to remove other declarations " + "but no other declaration present", xmlElement.getId(), xmlElement.getDocument().getSourceFile().print(true), xmlElement.getPosition().getStartLine() + 1
mergingReport.getLogger().verbose("Merging " + getId() + " with lower " + lowerPriorityNode.printPosition());
logger.verbose(lowerPriorityChild.getId() + " defined in both files...");