blob: 03daf401dce98d7b32d91a4691ae12dae3dd661e [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.ide.eclipse.adt.internal.sdk;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
import com.android.sdklib.SdkConstants;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Validator;
/**
* Manages the layout devices.
* They can come from 3 sources: built-in, add-ons, user.
*/
public class LayoutDeviceManager {
/**
* A SAX error handler that captures the errors and warnings.
* This allows us to capture *all* errors and just not get an exception on the first one.
*/
private static class CaptureErrorHandler implements ErrorHandler {
private final String mSourceLocation;
private boolean mFoundError = false;
CaptureErrorHandler(String sourceLocation) {
mSourceLocation = sourceLocation;
}
public boolean foundError() {
return mFoundError;
}
/**
* @throws SAXException
*/
public void error(SAXParseException ex) throws SAXException {
mFoundError = true;
AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
}
/**
* @throws SAXException
*/
public void fatalError(SAXParseException ex) throws SAXException {
mFoundError = true;
AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
}
/**
* @throws SAXException
*/
public void warning(SAXParseException ex) throws SAXException {
// ignore those for now.
}
}
private final SAXParserFactory mParserFactory;
private List<LayoutDevice> mDefaultLayoutDevices =
new ArrayList<LayoutDevice>();
private List<LayoutDevice> mAddOnLayoutDevices =
new ArrayList<LayoutDevice>();
private final List<LayoutDevice> mUserLayoutDevices =
new ArrayList<LayoutDevice>();
private List<LayoutDevice> mLayoutDevices;
LayoutDeviceManager() {
mParserFactory = SAXParserFactory.newInstance();
mParserFactory.setNamespaceAware(true);
}
public List<LayoutDevice> getCombinedList() {
return mLayoutDevices;
}
public List<LayoutDevice> getDefaultLayoutDevices() {
return mDefaultLayoutDevices;
}
public List<LayoutDevice> getAddOnLayoutDevice() {
return mAddOnLayoutDevices;
}
public List<LayoutDevice> getUserLayoutDevices() {
return mUserLayoutDevices;
}
public LayoutDevice getUserLayoutDevice(String name) {
for (LayoutDevice d : mUserLayoutDevices) {
if (d.getName().equals(name)) {
return d;
}
}
return null;
}
public LayoutDevice addUserDevice(String name, float xdpi, float ydpi) {
LayoutDevice d = new LayoutDevice(name);
d.setXDpi(xdpi);
d.setYDpi(ydpi);
mUserLayoutDevices.add(d);
combineLayoutDevices();
return d;
}
public void removeUserDevice(LayoutDevice device) {
if (mUserLayoutDevices.remove(device)) {
combineLayoutDevices();
}
}
/**
* Replaces a device with a new one with new name and/or x/y dpi, and return the new device.
* If the name and dpi values are identical the given device is returned an nothing is done
* @param device the {@link LayoutDevice} to replace
* @param newName the new name.
* @param newXDpi the new X dpi value
* @param newYDpi the new Y dpi value.
* @return the new LayoutDevice
*/
public LayoutDevice replaceUserDevice(LayoutDevice device, String newName,
float newXDpi, float newYDpi) {
if (device.getName().equals(newName) && device.getXDpi() == newXDpi &&
device.getYDpi() == newYDpi) {
return device;
}
// else create a new device
LayoutDevice newDevice = new LayoutDevice(newName);
newDevice.setXDpi(newXDpi);
newDevice.setYDpi(newYDpi);
// and get the Folderconfiguration
Map<String, FolderConfiguration> configs = device.getConfigs();
newDevice.addConfigs(configs);
// replace the old device with the new
mUserLayoutDevices.remove(device);
mUserLayoutDevices.add(newDevice);
combineLayoutDevices();
return newDevice;
}
/**
* Adds or replaces a configuration in a given {@link LayoutDevice}.
* @param device the device to modify
* @param configName the configuration name to add or replace
* @param config the configuration to set
*/
public void addUserConfiguration(LayoutDevice device, String configName,
FolderConfiguration config) {
// check that the device does belong to the user list.
// the main goal is to make sure that this does not belong to the default/addon list.
if (mUserLayoutDevices.contains(device)) {
device.addConfig(configName, config);
}
}
/**
* Replaces a configuration in a given {@link LayoutDevice}.
* @param device the device to modify
* @param oldConfigName the name of the config to replace. If null, the new config is simply
* added.
* @param newConfigName the configuration name to add or replace
* @param config the configuration to set
*/
public void replaceUserConfiguration(LayoutDevice device, String oldConfigName,
String newConfigName, FolderConfiguration config) {
// check that the device does belong to the user list.
// the main goal is to make sure that this does not belong to the default/addon list.
if (mUserLayoutDevices.contains(device)) {
// if the old and new config name are different, remove the old one
if (oldConfigName != null && oldConfigName.equals(newConfigName) == false) {
device.removeConfig(oldConfigName);
}
// and then add the new one
device.addConfig(newConfigName, config);
}
}
/**
* Removes a configuration from a given user {@link LayoutDevice}
* @param device the device to modify
* @param configName the name of the config to remove
*/
public void removeUserConfiguration(LayoutDevice device, String configName) {
// check that the device does belong to the user list.
// the main goal is to make sure that this does not belong to the default/addon list.
if (mUserLayoutDevices.contains(device)) {
device.removeConfig(configName);
}
}
void load(String sdkOsLocation) {
// load the default devices
loadDefaultLayoutDevices(sdkOsLocation);
// load the user devices;
}
void parseAddOnLayoutDevice(File deviceXml) {
parseLayoutDevices(deviceXml, mAddOnLayoutDevices);
}
void sealAddonLayoutDevices() {
mAddOnLayoutDevices = Collections.unmodifiableList(mAddOnLayoutDevices);
combineLayoutDevices();
}
/**
* Does the actual parsing of a devices.xml file.
*/
private void parseLayoutDevices(File deviceXml, List<LayoutDevice> list) {
// first we validate the XML
try {
Source source = new StreamSource(new FileReader(deviceXml));
CaptureErrorHandler errorHandler = new CaptureErrorHandler(deviceXml.getAbsolutePath());
Validator validator = LayoutConfigsXsd.getValidator(errorHandler);
validator.validate(source);
if (errorHandler.foundError() == false) {
// do the actual parsing
LayoutDeviceHandler handler = new LayoutDeviceHandler();
SAXParser parser = mParserFactory.newSAXParser();
parser.parse(new InputSource(new FileInputStream(deviceXml)), handler);
// get the parsed devices
list.addAll(handler.getDevices());
}
} catch (SAXException e) {
AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
} catch (FileNotFoundException e) {
// this shouldn't happen as we check above.
} catch (IOException e) {
AdtPlugin.log(e, "Error reading %1$s", deviceXml.getAbsoluteFile());
} catch (ParserConfigurationException e) {
AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
}
}
/**
* Creates some built-it layout devices.
*/
private void loadDefaultLayoutDevices(String sdkOsLocation) {
ArrayList<LayoutDevice> list = new ArrayList<LayoutDevice>();
File toolsFolder = new File(sdkOsLocation, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER);
if (toolsFolder.isDirectory()) {
File deviceXml = new File(toolsFolder, SdkConstants.FN_DEVICES_XML);
if (deviceXml.isFile()) {
parseLayoutDevices(deviceXml, list);
}
}
mDefaultLayoutDevices = Collections.unmodifiableList(list);
}
private void combineLayoutDevices() {
ArrayList<LayoutDevice> list = new ArrayList<LayoutDevice>();
list.addAll(mDefaultLayoutDevices);
list.addAll(mAddOnLayoutDevices);
list.addAll(mUserLayoutDevices);
mLayoutDevices = Collections.unmodifiableList(list);
}
}