com.google.devtools.build.android.xml.PublicXmlResourceValue.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.android.xml.PublicXmlResourceValue.java

Source

// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.android.xml;

import com.android.SdkConstants;
import com.android.resources.ResourceType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidResourceClassWriter;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
import com.google.devtools.build.android.XmlResourceValues;
import com.google.devtools.build.android.proto.SerializeFormat;
import java.io.IOException;
import java.io.OutputStream;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

/**
 * Represents an Android resource <public> xml tag.
 *
 * <p>This is used to declare a resource public and reserve a fixed ID for a resource. It is
 * generally undocumented (update this if we ever get a doc), but used heavily by the android
 * framework resources. One description of it is at the android <a
 * href="http://tools.android.com/tech-docs/private-resources">tools site</a>. Public tags can be
 * defined in any xml file in the values folder. <code>
 *   &lt;resources&gt;
 *     &lt;string name="mypublic_string"&gt; Pub &lt;/string&gt;
 *     &lt;public name="mypublic_string" type="string" id="0x7f050004" /&gt;
 *     &lt;string name="myother_string"&gt; the others &lt;/string&gt;
 *     &lt;public name="myother_string" type="string" /&gt;
 *  &lt;/resources&gt;
 * </code> The "id" attribute is optional if an earlier public tag has already specified an "id"
 * attribute. In such cases, ID assignment will continue from the previous reserved ID.
 */
public class PublicXmlResourceValue implements XmlResourceValue {

    private final Map<ResourceType, Optional<Integer>> typeToId;
    private static final String MISSING_ID_VALUE = "";

    private PublicXmlResourceValue(Map<ResourceType, Optional<Integer>> typeToId) {
        this.typeToId = typeToId;
    }

    public static PublicXmlResourceValue of(Map<ResourceType, Optional<Integer>> typeToId) {
        return new PublicXmlResourceValue(typeToId);
    }

    public static XmlResourceValue create(ResourceType type, Optional<Integer> id) {
        Map<ResourceType, Optional<Integer>> map = new EnumMap<>(ResourceType.class);
        map.put(type, id);
        return new PublicXmlResourceValue(map);
    }

    @Override
    public void write(FullyQualifiedName key, DataSource source, AndroidDataWritingVisitor mergedDataWriter) {
        for (Entry<ResourceType, Optional<Integer>> entry : typeToId.entrySet()) {
            Integer value = entry.getValue().orNull();
            mergedDataWriter.define(key).derivedFrom(source).startTag(ResourceType.PUBLIC.getName()).named(key)
                    .attribute(SdkConstants.ATTR_TYPE).setTo(entry.getKey().toString()).optional()
                    .attribute(SdkConstants.ATTR_ID).setTo(value == null ? null : "0x" + Integer.toHexString(value))
                    .closeUnaryTag().save();
        }
    }

    @Override
    public void writeResourceToClass(FullyQualifiedName key, AndroidResourceClassWriter resourceClassWriter) {
        for (Entry<ResourceType, Optional<Integer>> entry : typeToId.entrySet()) {
            resourceClassWriter.writePublicValue(entry.getKey(), key.name(), entry.getValue());
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(typeToId);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof PublicXmlResourceValue)) {
            return false;
        }
        PublicXmlResourceValue other = (PublicXmlResourceValue) obj;
        return Objects.equals(typeToId, other.typeToId);
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(getClass()).add("typeToId: ", typeToId).toString();
    }

    @SuppressWarnings("deprecation")
    public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
        Map<String, String> protoValues = proto.getMappedStringValue();
        ImmutableMap.Builder<ResourceType, Optional<Integer>> typeToId = ImmutableMap.builder();
        for (Entry<String, String> entry : protoValues.entrySet()) {
            ResourceType type = ResourceType.getEnum(entry.getKey());
            Preconditions.checkNotNull(type);
            Optional<Integer> id = MISSING_ID_VALUE.equals(entry.getValue()) ? Optional.<Integer>absent()
                    : Optional.of(Integer.decode(entry.getValue()));
            typeToId.put(type, id);
        }
        return of(typeToId.build());
    }

    @Override
    public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) throws IOException {
        Map<String, String> assignments = Maps.newLinkedHashMapWithExpectedSize(typeToId.size());
        for (Entry<ResourceType, Optional<Integer>> entry : typeToId.entrySet()) {
            Optional<Integer> value = entry.getValue();
            String stringValue = value.isPresent() ? value.get().toString() : MISSING_ID_VALUE;
            assignments.put(entry.getKey().toString(), stringValue);
        }
        SerializeFormat.DataValue.Builder builder = XmlResourceValues.newSerializableDataValueBuilder(sourceId);
        builder.setXmlValue(builder.getXmlValueBuilder().setType(SerializeFormat.DataValueXml.XmlType.PUBLIC)
                .putAllNamespace(namespaces.asMap()).putAllMappedStringValue(assignments));
        return XmlResourceValues.serializeProtoDataValue(output, builder);
    }

    @Override
    public XmlResourceValue combineWith(XmlResourceValue value) {
        if (!(value instanceof PublicXmlResourceValue)) {
            throw new IllegalArgumentException(value + "is not combinable with " + this);
        }
        PublicXmlResourceValue other = (PublicXmlResourceValue) value;
        Map<ResourceType, Optional<Integer>> combined = new EnumMap<>(ResourceType.class);
        combined.putAll(typeToId);
        for (Entry<ResourceType, Optional<Integer>> entry : other.typeToId.entrySet()) {
            Optional<Integer> existing = combined.get(entry.getKey());
            if (existing != null && !existing.equals(entry.getValue())) {
                throw new IllegalArgumentException(
                        String.format("Public resource of type %s assigned two different id values 0x%x and 0x%x",
                                entry.getKey(), existing.orNull(), entry.getValue().orNull()));
            }
            combined.put(entry.getKey(), entry.getValue());
        }
        return of(combined);
    }
}