diff options
Diffstat (limited to 'PrintServiceStubs/src/com/android/printservicestubs/VendorConfig.java')
-rw-r--r-- | PrintServiceStubs/src/com/android/printservicestubs/VendorConfig.java | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/PrintServiceStubs/src/com/android/printservicestubs/VendorConfig.java b/PrintServiceStubs/src/com/android/printservicestubs/VendorConfig.java new file mode 100644 index 0000000..31ef3f5 --- /dev/null +++ b/PrintServiceStubs/src/com/android/printservicestubs/VendorConfig.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2016 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.printservicestubs; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.XmlResourceParser; +import android.util.ArrayMap; +import com.android.internal.annotations.Immutable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Vendor configuration as read from {@link R.xml#vendorconfigs vendorconfigs.xml}. Configuration + * can be read via {@link #getConfig(Context, String)}. + */ +@Immutable +public class VendorConfig { + /** + * Lock for {@link #sConfigs} + */ + private static final Object sLock = new Object(); + + /** + * Strings used as XML tags + */ + private static final String VENDORS_TAG = "vendors"; + private static final String VENDOR_TAG = "vendor"; + private static final String NAME_TAG = "name"; + private static final String PACKAGE_TAG = "package"; + private static final String MDNSNAMES_TAG = "mdns-names"; + private static final String MDNSNAME_TAG = "mdns-name"; + + /** + * Map from vendor name to config. Initialized on first {@link #getConfig use}. + */ + private static @Nullable ArrayMap<String, VendorConfig> sConfigs; + + /** + * Localized vendor name + */ + private final @NonNull String mName; + + /** + * Package name containing the print service for this vendor + */ + private final @NonNull String mPackageName; + + /** + * mDNS names used by this vendor + */ + private final @NonNull List<String> mMDNSNames; + + /** + * Create an immutable configuration. + */ + private VendorConfig(@NonNull String name, @NonNull String packageName, + @NonNull List<String> mDNSNames) { + mName = name; + mPackageName = packageName; + mMDNSNames = mDNSNames; + } + + /** + * Get the configuration for a vendor. + * + * @param context Calling context + * @param name The name of the config to read + * + * @return the config for the vendor or null if not found + * + * @throws IOException + * @throws XmlPullParserException + */ + public static @Nullable VendorConfig getConfig(@NonNull Context context, @NonNull String name) + throws IOException, XmlPullParserException { + synchronized (sLock) { + if (sConfigs == null) { + sConfigs = readVendorConfigs(context); + } + + return sConfigs.get(name); + } + } + + /** + * Get all known vendor configurations. + * + * @param context Calling context + * + * @return The known configurations + * + * @throws IOException + * @throws XmlPullParserException + */ + public static @NonNull Collection<VendorConfig> getAllConfigs(@NonNull Context context) + throws IOException, XmlPullParserException { + synchronized (sLock) { + if (sConfigs == null) { + sConfigs = readVendorConfigs(context); + } + + return sConfigs.values(); + } + } + + /** + * Read the text from a XML tag. + * + * @param parser XML parser to read from + * + * @return The test or "" if no text was found + * + * @throws IOException + * @throws XmlPullParserException + */ + private static @NonNull String readText(XmlPullParser parser) + throws IOException, XmlPullParserException { + String result = ""; + + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + + return result; + } + + /** + * Read a tag with a text content from the parser. + * + * @param parser XML parser to read from + * @param tagName The name of the tag to read + * + * @return The text content of the tag + * + * @throws IOException + * @throws XmlPullParserException + */ + private static @NonNull String readSimpleTag(@NonNull Context context, + @NonNull XmlPullParser parser, @NonNull String tagName, boolean resolveReferences) + throws IOException, XmlPullParserException { + parser.require(XmlPullParser.START_TAG, null, tagName); + String text = readText(parser); + parser.require(XmlPullParser.END_TAG, null, tagName); + + if (resolveReferences && text.startsWith("@")) { + return context.getResources().getString( + context.getResources().getIdentifier(text, null, context.getPackageName())); + } else { + return text; + } + } + + /** + * Read content of a list of tags. + * + * @param parser XML parser to read from + * @param tagName The name of the list tag + * @param subTagName The name of the list-element tags + * @param tagReader The {@link TagReader reader} to use to read the tag content + * @param <T> The type of the parsed tag content + * + * @return A list of {@link T} + * + * @throws XmlPullParserException + * @throws IOException + */ + private static @NonNull <T> ArrayList<T> readTagList(@NonNull XmlPullParser parser, + @NonNull String tagName, @NonNull String subTagName, @NonNull TagReader<T> tagReader) + throws XmlPullParserException, IOException { + ArrayList<T> entries = new ArrayList<>(); + + parser.require(XmlPullParser.START_TAG, null, tagName); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + + if (parser.getName().equals(subTagName)) { + entries.add(tagReader.readTag(parser, subTagName)); + } else { + throw new XmlPullParserException( + "Unexpected subtag of " + tagName + ": " + parser.getName()); + } + } + + return entries; + } + + /** + * Read the vendor configuration file. + * + * @param context The content issuing the read + * + * @return An map pointing from vendor name to config + * + * @throws IOException + * @throws XmlPullParserException + */ + private static @NonNull ArrayMap<String, VendorConfig> readVendorConfigs( + @NonNull final Context context) throws IOException, XmlPullParserException { + try (XmlResourceParser parser = context.getResources().getXml(R.xml.vendorconfigs)) { + // Skip header + int parsingEvent; + do { + parsingEvent = parser.next(); + } while (parsingEvent != XmlResourceParser.START_TAG); + + ArrayList<VendorConfig> configs = readTagList(parser, VENDORS_TAG, VENDOR_TAG, + new TagReader<VendorConfig>() { + public VendorConfig readTag(XmlPullParser parser, String tagName) + throws XmlPullParserException, IOException { + return readVendorConfig(context, parser, tagName); + } + }); + + ArrayMap<String, VendorConfig> configMap = new ArrayMap<>(configs.size()); + final int numConfigs = configs.size(); + for (int i = 0; i < numConfigs; i++) { + VendorConfig config = configs.get(i); + + configMap.put(config.mName, config); + } + + return configMap; + } + } + + /** + * Read a single vendor configuration. + * + * @param parser XML parser to read from + * @param tagName The vendor tag + * @param context Calling context + * + * @return A config + * + * @throws XmlPullParserException + * @throws IOException + */ + private static VendorConfig readVendorConfig(@NonNull final Context context, + @NonNull XmlPullParser parser, @NonNull String tagName) throws XmlPullParserException, + IOException { + parser.require(XmlPullParser.START_TAG, null, tagName); + + String name = null; + String packageName = null; + List<String> mDNSNames = null; + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + + String subTagName = parser.getName(); + + switch (subTagName) { + case NAME_TAG: + name = readSimpleTag(context, parser, NAME_TAG, false); + break; + case PACKAGE_TAG: + packageName = readSimpleTag(context, parser, PACKAGE_TAG, true); + break; + case MDNSNAMES_TAG: + mDNSNames = readTagList(parser, MDNSNAMES_TAG, MDNSNAME_TAG, + new TagReader<String>() { + public String readTag(XmlPullParser parser, String tagName) + throws XmlPullParserException, IOException { + return readSimpleTag(context, parser, tagName, true); + } + } + ); + break; + default: + throw new XmlPullParserException("Unexpected subtag of " + tagName + ": " + + subTagName); + + } + } + + if (name == null) { + throw new XmlPullParserException("name is required"); + } + + if (packageName == null) { + throw new XmlPullParserException("package is required"); + } + + if (mDNSNames == null) { + mDNSNames = Collections.emptyList(); + } + + // A vendor config should be immutable + mDNSNames = Collections.unmodifiableList(mDNSNames); + + return new VendorConfig(name, packageName, mDNSNames); + } + + /** + * Get the name of the vendor. + * + * @return The name of the vendor + */ + public String getName() { + return mName; + } + + /** + * Get the package name of the print service of the vendor. + * + * @return The package name of the print service + */ + public String getPackageName() { + return mPackageName; + } + + /** + * Get the mDNS names associated with the vendor. + * + * @return The list if mDNS names + */ + public List<String> getMDNSNames() { + return mMDNSNames; + } + + @Override + public String toString() { + return mName + " -> " + mPackageName + ", " + mMDNSNames; + } + + /** + * Used a a "function pointer" when reading a tag in {@link #readTagList(XmlPullParser, String, + * String, TagReader)}. + * + * @param <T> The type of content to read + */ + private interface TagReader<T> { + T readTag(XmlPullParser parser, String tagName) throws XmlPullParserException, IOException; + } +} |