blob: 5a4d9d259677b33ea70083640e0f5916b5530291 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.manifmerger;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_NAME;
import static com.android.manifmerger.AttributeModel.Hexadecimal32BitsWithMinimumValue;
import static com.android.manifmerger.AttributeModel.MultiValueValidator;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.concurrency.Immutable;
import com.android.utils.SdkUtils;
import com.android.xml.AndroidManifest;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Model for the manifest file merging activities.
* <p>
*
* This model will describe each element that is eligible for merging and associated merging
* policies. It is not reusable as most of its interfaces are private but a future enhancement
* could easily make this more generic/reusable if we need to merge more than manifest files.
*
*/
@Immutable
class ManifestModel {
/**
* Interface responsible for providing a key extraction capability from a xml element.
* Some elements store their keys as an attribute, some as a sub-element attribute, some don't
* have any key.
*/
@Immutable
interface NodeKeyResolver {
/**
* Returns the key associated with this xml element.
* @param xmlElement the xml element to get the key from
* @return the key as a string to uniquely identify xmlElement from similarly typed elements
* in the xml document or null if there is no key.
*/
@Nullable String getKey(Element xmlElement);
/**
* Returns the attribute(s) used to store the xml element key.
* @return the key attribute(s) name(s) or null of this element does not have a key.
*/
@NonNull
ImmutableList<String> getKeyAttributesNames();
}
/**
* Implementation of {@link com.android.manifmerger.ManifestModel.NodeKeyResolver} that do not
* provide any key (the element has to be unique in the xml document).
*/
private static class NoKeyNodeResolver implements NodeKeyResolver {
@Override
@Nullable
public String getKey(Element xmlElement) {
return null;
}
@NonNull
@Override
public ImmutableList<String> getKeyAttributesNames() {
return ImmutableList.of();
}
}
/**
* Implementation of {@link com.android.manifmerger.ManifestModel.NodeKeyResolver} that uses an
* attribute to resolve the key value.
*/
private static class AttributeBasedNodeKeyResolver implements NodeKeyResolver {
@Nullable private final String mNamespaceUri;
private final String mAttributeName;
/**
* Build a new instance capable of resolving an xml element key from the passed attribute
* namespace and local name.
* @param namespaceUri optional namespace for the attribute name.
* @param attributeName attribute name
*/
private AttributeBasedNodeKeyResolver(@Nullable String namespaceUri,
@NonNull String attributeName) {
this.mNamespaceUri = namespaceUri;
this.mAttributeName = Preconditions.checkNotNull(attributeName);
}
@Override
@Nullable
public String getKey(Element xmlElement) {
String key = mNamespaceUri == null
? xmlElement.getAttribute(mAttributeName)
: xmlElement.getAttributeNS(mNamespaceUri, mAttributeName);
if (Strings.isNullOrEmpty(key)) return null;
return key;
}
@NonNull
@Override
public ImmutableList<String> getKeyAttributesNames() {
return ImmutableList.of(mAttributeName);
}
}
/**
* Subclass of {@link com.android.manifmerger.ManifestModel.AttributeBasedNodeKeyResolver} that
* uses "android:name" as the attribute.
*/
private static final NodeKeyResolver DEFAULT_NAME_ATTRIBUTE_RESOLVER =
new AttributeBasedNodeKeyResolver(ANDROID_URI, SdkConstants.ATTR_NAME);
private static final NoKeyNodeResolver DEFAULT_NO_KEY_NODE_RESOLVER = new NoKeyNodeResolver();
/**
* A {@link com.android.manifmerger.ManifestModel.NodeKeyResolver} capable of extracting the
* element key first in an "android:name" attribute and if not value found there, in the
* "android:glEsVersion" attribute.
*/
private static final NodeKeyResolver NAME_AND_GLESVERSION_KEY_RESOLVER = new NodeKeyResolver() {
private final NodeKeyResolver nameAttrResolver = DEFAULT_NAME_ATTRIBUTE_RESOLVER;
private final NodeKeyResolver glEsVersionResolver =
new AttributeBasedNodeKeyResolver(ANDROID_URI,
AndroidManifest.ATTRIBUTE_GLESVERSION);
@Nullable
@Override
public String getKey(Element xmlElement) {
String key = nameAttrResolver.getKey(xmlElement);
return Strings.isNullOrEmpty(key)
? glEsVersionResolver.getKey(xmlElement)
: key;
}
@NonNull
@Override
public ImmutableList<String> getKeyAttributesNames() {
return ImmutableList.of(SdkConstants.ATTR_NAME, AndroidManifest.ATTRIBUTE_GLESVERSION);
}
};
/**
* Specific {@link com.android.manifmerger.ManifestModel.NodeKeyResolver} for intent-filter
* elements.
* Intent filters do not have a proper key, therefore their identity is really carried by
* the presence of the action and category sub-elements.
* We concatenate such elements sub-keys (after sorting them to work around declaration order)
* and use that for the intent-filter unique key.
*/
private static final NodeKeyResolver INTENT_FILTER_KEY_RESOLVER = new NodeKeyResolver() {
@Nullable
@Override
public String getKey(Element element) {
OrphanXmlElement xmlElement = new OrphanXmlElement(element);
assert(xmlElement.getType() == NodeTypes.INTENT_FILTER);
// concatenate all actions and categories attribute names.
List<String> allSubElementKeys = new ArrayList<String>();
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE) continue;
OrphanXmlElement subElement = new OrphanXmlElement((Element) child);
if (subElement.getType() == NodeTypes.ACTION
|| subElement.getType() == NodeTypes.CATEGORY) {
Attr nameAttribute = subElement.getXml()
.getAttributeNodeNS(ANDROID_URI, ATTR_NAME);
if (nameAttribute != null) {
allSubElementKeys.add(nameAttribute.getValue());
}
}
}
Collections.sort(allSubElementKeys);
return Joiner.on('+').join(allSubElementKeys);
}
@NonNull
@Override
public ImmutableList<String> getKeyAttributesNames() {
return ImmutableList.of("action#name", "category#name");
}
};
/**
* Implementation of {@link com.android.manifmerger.ManifestModel.NodeKeyResolver} that
* combined two attributes values to create the key value.
*/
private static final class TwoAttributesBasedKeyResolver implements NodeKeyResolver {
private final NodeKeyResolver firstAttributeKeyResolver;
private final NodeKeyResolver secondAttributeKeyResolver;
private TwoAttributesBasedKeyResolver(NodeKeyResolver firstAttributeKeyResolver,
NodeKeyResolver secondAttributeKeyResolver) {
this.firstAttributeKeyResolver = firstAttributeKeyResolver;
this.secondAttributeKeyResolver = secondAttributeKeyResolver;
}
@Nullable
@Override
public String getKey(Element xmlElement) {
String firstKey = firstAttributeKeyResolver.getKey(xmlElement);
String secondKey = secondAttributeKeyResolver.getKey(xmlElement);
return Strings.isNullOrEmpty(firstKey)
? secondKey
: Strings.isNullOrEmpty(secondKey)
? firstKey
: firstKey + "+" + secondKey;
}
@NonNull
@Override
public ImmutableList<String> getKeyAttributesNames() {
return ImmutableList.of(firstAttributeKeyResolver.getKeyAttributesNames().get(0),
secondAttributeKeyResolver.getKeyAttributesNames().get(0));
}
}
private static final AttributeModel.BooleanValidator BOOLEAN_VALIDATOR =
new AttributeModel.BooleanValidator();
private static final boolean MULTIPLE_DECLARATION_FOR_SAME_KEY_ALLOWED = true;
/**
* Definitions of the support node types in the Android Manifest file.
* {@link <a href=http://developer.android.com/guide/topics/manifest/manifest-intro.html/>}
* for more details about the xml format.
*
* There is no DTD or schema associated with the file type so this is best effort in providing
* some metadata on the elements of the Android's xml file.
*
* Each xml element is defined as an enum value and for each node, extra metadata is added
* <ul>
* <li>{@link com.android.manifmerger.MergeType} to identify how the merging engine
* should process this element.</li>
* <li>{@link com.android.manifmerger.ManifestModel.NodeKeyResolver} to resolve the
* element's key. Elements can have an attribute like "android:name", others can use
* a sub-element, and finally some do not have a key and are meant to be unique.</li>
* <li>List of attributes models with special behaviors :
* <ul>
* <li>Smart substitution of class names to fully qualified class names using the
* document's package declaration. The list's size can be 0..n</li>
* <li>Implicit default value when no defined on the xml element.</li>
* <li>{@link AttributeModel.Validator} to validate attribute value against.</li>
* </ul>
* </ul>
*
* It is of the outermost importance to keep this model correct as it is used by the merging
* engine to make all its decisions. There should not be special casing in the engine, all
* decisions must be represented here.
*
* If you find yourself needing to extend the model to support future requirements, do it here
* and modify the engine to make proper decision based on the added metadata.
*/
enum NodeTypes {
/**
* Action (contained in intent-filter)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/action-element.html>
* Action Xml documentation</a>}
*/
ACTION(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER),
/**
* Activity (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/activity-element.html>
* Activity Xml documentation</a>}
*/
ACTIVITY(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel("parentActivityName").setIsPackageDependent(),
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Activity-alias (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/activity-alias-element.html>
* Activity-alias Xml documentation</a>}
*/
ACTIVITY_ALIAS(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel("targetActivity").setIsPackageDependent(),
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Application (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/application-element.html>
* Application Xml documentation</a>}
*/
APPLICATION(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER,
AttributeModel.newModel("backupAgent").setIsPackageDependent(),
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Category (contained in intent-filter)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/category-element.html>
* Category Xml documentation</a>}
*/
CATEGORY(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER),
/**
* Compatible-screens (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/compatible-screens-element.html>
* Category Xml documentation</a>}
*/
COMPATIBLE_SCREENS(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Data (contained in intent-filter)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/data-element.html>
* Category Xml documentation</a>}
*/
DATA(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Grant-uri-permission (contained in intent-filter)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/grant-uri-permission-element.html>
* Category Xml documentation</a>}
*/
GRANT_URI_PERMISSION(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Instrumentation (contained in intent-filter)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/instrumentation-element.html>
* Instrunentation Xml documentation</a>}
*/
INSTRUMENTATION(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Intent-filter (contained in activity, activity-alias, service, receiver)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/intent-filter-element.html>
* Intent-filter Xml documentation</a>}
*/
INTENT_FILTER(MergeType.ALWAYS, INTENT_FILTER_KEY_RESOLVER,
MULTIPLE_DECLARATION_FOR_SAME_KEY_ALLOWED),
/**
* Manifest (top level node)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/manifest-element.html>
* Manifest Xml documentation</a>}
*/
MANIFEST(MergeType.MERGE_CHILDREN_ONLY, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Meta-data (contained in activity, activity-alias, application, provider, receiver)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/meta-data-element.html>
* Meta-data Xml documentation</a>}
*/
META_DATA(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER),
/**
* Path-permission (contained in provider)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/path-permission-element.html>
* Meta-data Xml documentation</a>}
*/
PATH_PERMISSION(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Permission-group (contained in manifest).
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/permission-group-element.html>
* Permission-group Xml documentation</a>}
*
*/
PERMISSION_GROUP(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME)),
/**
* Permission (contained in manifest).
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/permission-element.html>
* Permission Xml documentation</a>}
*
*/
PERMISSION(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME),
AttributeModel.newModel("protectionLevel")
.setDefaultValue("normal")
// TODO : this will need to be populated from
// sdk/platforms/android-19/data/res/values.attrs_manifest.xml
.setOnReadValidator(new MultiValueValidator(
"normal", "dangerous", "signature", "signatureOrSystem"))),
/**
* Permission-tree (contained in manifest).
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/permission-tree-element.html>
* Permission-tree Xml documentation</a>}
*
*/
PERMISSION_TREE(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME)),
/**
* Provider (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/provider-element.html>
* Provider Xml documentation</a>}
*/
PROVIDER(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME)
.setIsPackageDependent()),
/**
* Receiver (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/receiver-element.html>
* Receiver Xml documentation</a>}
*/
RECEIVER(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Screen (contained in compatible-screens)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/compatible-screens-element.html>
* Receiver Xml documentation</a>}
*/
SCREEN(MergeType.MERGE, new TwoAttributesBasedKeyResolver(
new AttributeBasedNodeKeyResolver(ANDROID_URI, "screenSize"),
new AttributeBasedNodeKeyResolver(ANDROID_URI, "screenDensity"))),
/**
* Service (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/application-element.html>
* Service Xml documentation</a>}
*/
SERVICE(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(SdkConstants.ATTR_NAME).setIsPackageDependent()),
/**
* Supports-gl-texture (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/supports-gl-texture-element.html>
* Support-screens Xml documentation</a>}
*/
SUPPORTS_GL_TEXTURE(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER),
/**
* Support-screens (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/supports-screens-element.html>
* Support-screens Xml documentation</a>}
*/
SUPPORTS_SCREENS(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Uses-configuration (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/uses-configuration-element.html>
* Support-screens Xml documentation</a>}
*/
USES_CONFIGURATION(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER),
/**
* Uses-feature (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/uses-feature-element.html>
* Uses-feature Xml documentation</a>}
*/
USES_FEATURE(MergeType.MERGE, NAME_AND_GLESVERSION_KEY_RESOLVER,
AttributeModel.newModel(AndroidManifest.ATTRIBUTE_REQUIRED)
.setDefaultValue(SdkConstants.VALUE_TRUE)
.setOnReadValidator(BOOLEAN_VALIDATOR)
.setMergingPolicy(AttributeModel.OR_MERGING_POLICY),
AttributeModel.newModel(AndroidManifest.ATTRIBUTE_GLESVERSION)
.setDefaultValue("0x00010000")
.setOnReadValidator(new Hexadecimal32BitsWithMinimumValue(0x00010000))),
/**
* Use-library (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/uses-library-element.html>
* Use-library Xml documentation</a>}
*/
USES_LIBRARY(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER,
AttributeModel.newModel(AndroidManifest.ATTRIBUTE_REQUIRED)
.setDefaultValue(SdkConstants.VALUE_TRUE)
.setOnReadValidator(BOOLEAN_VALIDATOR)
.setMergingPolicy(AttributeModel.OR_MERGING_POLICY)),
/**
* Uses-permission (contained in application)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/uses-permission-element.html>
* Uses-permission Xml documentation</a>}
*/
USES_PERMISSION(MergeType.MERGE, DEFAULT_NAME_ATTRIBUTE_RESOLVER),
/**
* Uses-sdk (contained in manifest)
* <br>
* <b>See also : </b>
* {@link <a href=http://developer.android.com/guide/topics/manifest/uses-sdk-element.html>
* Uses-sdk Xml documentation</a>}
*/
USES_SDK(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER,
AttributeModel.newModel("minSdkVersion")
.setDefaultValue(SdkConstants.VALUE_1)
.setMergingPolicy(AttributeModel.NO_MERGING_POLICY),
AttributeModel.newModel("maxSdkVersion")
.setMergingPolicy(AttributeModel.NO_MERGING_POLICY),
// TODO : model target's default value is minSdkVersion value.
AttributeModel.newModel("targetSdkVersion")
.setMergingPolicy(AttributeModel.NO_MERGING_POLICY)
),
/**
* Custom tag for any application specific element
*/
CUSTOM(MergeType.MERGE, DEFAULT_NO_KEY_NODE_RESOLVER);
private final MergeType mMergeType;
private final NodeKeyResolver mNodeKeyResolver;
private final ImmutableList<AttributeModel> mAttributeModels;
private final boolean mMultipleDeclarationAllowed;
private NodeTypes(
@NonNull MergeType mergeType,
@NonNull NodeKeyResolver nodeKeyResolver,
@Nullable AttributeModel.Builder... attributeModelBuilders) {
this(mergeType, nodeKeyResolver, false, attributeModelBuilders);
}
private NodeTypes(
@NonNull MergeType mergeType,
@NonNull NodeKeyResolver nodeKeyResolver,
boolean mutipleDeclarationAllowed,
@Nullable AttributeModel.Builder... attributeModelBuilders) {
this.mMergeType = Preconditions.checkNotNull(mergeType);
this.mNodeKeyResolver = Preconditions.checkNotNull(nodeKeyResolver);
ImmutableList.Builder<AttributeModel> attributeModels =
new ImmutableList.Builder<AttributeModel>();
if (attributeModelBuilders != null) {
for (AttributeModel.Builder attributeModelBuilder : attributeModelBuilders) {
attributeModels.add(attributeModelBuilder.build());
}
}
this.mAttributeModels = attributeModels.build();
this.mMultipleDeclarationAllowed = mutipleDeclarationAllowed;
}
@NonNull
NodeKeyResolver getNodeKeyResolver() {
return mNodeKeyResolver;
}
ImmutableList<AttributeModel> getAttributeModels() {
return mAttributeModels.asList();
}
@Nullable
AttributeModel getAttributeModel(XmlNode.NodeName attributeName) {
// mAttributeModels could be replaced with a Map if the number of models grows.
for (AttributeModel attributeModel : mAttributeModels) {
if (attributeModel.getName().equals(attributeName)) {
return attributeModel;
}
}
return null;
}
/**
* Returns the Xml name for this node type
*/
String toXmlName() {
return SdkUtils.constantNameToXmlName(this.name());
}
/**
* Returns the {@link NodeTypes} instance from an xml element name (without namespace
* decoration). For instance, an xml element
* <pre>
* {@code
* <activity android:name="foo">
* ...
* </activity>}
* </pre>
* has a xml simple name of "activity" which will resolve to {@link NodeTypes#ACTIVITY} value.
*
* Note : a runtime exception will be generated if no mapping from the simple name to a
* {@link com.android.manifmerger.ManifestModel.NodeTypes} exists.
*
* @param xmlSimpleName the xml (lower-hyphen separated words) simple name.
* @return the {@link NodeTypes} associated with that element name.
*/
static NodeTypes fromXmlSimpleName(String xmlSimpleName) {
String constantName = SdkUtils.xmlNameToConstantName(xmlSimpleName);
try {
return NodeTypes.valueOf(constantName);
} catch (IllegalArgumentException e) {
// if this element name is not a known tag, we categorize it as 'custom' which will
// be simply merged. It will prevent us from catching simple spelling mistakes but
// extensibility is a must have feature.
return NodeTypes.CUSTOM;
}
}
MergeType getMergeType() {
return mMergeType;
}
/**
* Returns true if multiple declaration for the same type and key are allowed or false if
* there must be only one declaration of this element for a particular key value.
*/
boolean areMultipleDeclarationAllowed() {
return mMultipleDeclarationAllowed;
}
}
}