| // 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; |
| |
| import com.android.SdkConstants; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer; |
| import com.google.devtools.build.android.xml.IdXmlResourceValue; |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.Iterator; |
| import javax.xml.stream.XMLEventReader; |
| import javax.xml.stream.XMLInputFactory; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.events.Attribute; |
| import javax.xml.stream.events.StartElement; |
| import javax.xml.stream.events.XMLEvent; |
| |
| /** |
| * Parses an XML file for "@+id/foo" and creates {@link IdXmlResourceValue} from parsed IDs. This |
| * can be a layout file, menu, drawable, etc. |
| */ |
| public class DataValueFileWithIds { |
| |
| public static void parse( |
| XMLInputFactory xmlInputFactory, |
| Path source, |
| FullyQualifiedName fileKey, |
| FullyQualifiedName.Factory fqnFactory, |
| KeyValueConsumer<DataKey, DataResource> overwritingConsumer, |
| KeyValueConsumer<DataKey, DataResource> combiningConsumer) |
| throws IOException, XMLStreamException { |
| ImmutableSet.Builder<String> newIds = ImmutableSet.builder(); |
| try (BufferedInputStream inStream = new BufferedInputStream(Files.newInputStream(source))) { |
| XMLEventReader eventReader = |
| xmlInputFactory.createXMLEventReader(inStream, StandardCharsets.UTF_8.toString()); |
| // Go through every start tag and look at the values of all attributes (the attribute does |
| // not need to be android:id, e.g., android:layout_above="@+id/UpcomingView"). |
| // If the any attribute's value begins with @+id/, then track it, since aapt seems to be |
| // forgiving and allow even non-android namespaced attributes to define a new ID. |
| while (eventReader.hasNext()) { |
| XMLEvent event = eventReader.nextEvent(); |
| if (event.isStartElement()) { |
| StartElement start = event.asStartElement(); |
| Iterator<Attribute> attributes = XmlResourceValues.iterateAttributesFrom(start); |
| while (attributes.hasNext()) { |
| Attribute attribute = attributes.next(); |
| String value = attribute.getValue(); |
| if (value.startsWith(SdkConstants.NEW_ID_PREFIX)) { |
| String idName = value.substring(SdkConstants.NEW_ID_PREFIX.length()); |
| newIds.add(idName); |
| } |
| } |
| } |
| } |
| eventReader.close(); |
| } catch (XMLStreamException e) { |
| if (e.getLocation() != null) { |
| throw new XMLStreamException(source + ": " + e.getMessage(), e.getLocation(), e); |
| } else { |
| throw new XMLStreamException(source + ": " + e.getMessage(), e); |
| } |
| |
| } catch (RuntimeException e) { |
| throw new RuntimeException("Error parsing " + source, e); |
| } |
| overwritingConsumer.accept(fileKey, DataValueFile.of(source)); |
| for (String id : newIds.build()) { |
| combiningConsumer.accept( |
| fqnFactory.parse("id/" + id), |
| DataResourceXml.createWithNoNamespace(source, IdXmlResourceValue.of())); |
| } |
| } |
| } |