aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-07-28 17:01:55 -0700
committerXavier Ducrohet <xav@android.com>2010-07-29 15:42:14 -0700
commit9498d202b8fbe9a7eff9ee209dc0575f8d8fac6d (patch)
treedac0e590e6983344ffb609211f7e7e4ac47a32b2 /eclipse/plugins
parent79de49d3c4108681d7a0381a4ff41ad70be24283 (diff)
downloadsdk-9498d202b8fbe9a7eff9ee209dc0575f8d8fac6d.zip
sdk-9498d202b8fbe9a7eff9ee209dc0575f8d8fac6d.tar.gz
sdk-9498d202b8fbe9a7eff9ee209dc0575f8d8fac6d.tar.bz2
Don't rely on map to store layout device configs.
Relying on maps means that we don't control the order of the configs in the UI. Mac/Windows show a different order already, and it's just luck that the Mac one matches the content of the XML. Now we keep the order from the XML and display it as-is in the UI. Change-Id: I900f330a4ea9059fe27df2bca6c3a7a770737ae4
Diffstat (limited to 'eclipse/plugins')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java72
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java167
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java5
4 files changed, 193 insertions, 83 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
index 8b585dd..6bfa9da 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfi
import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice.DeviceConfig;
import com.android.sdkuilib.ui.GridDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
@@ -47,7 +48,7 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
-import java.util.Map;
+import java.util.List;
import java.util.Map.Entry;
/**
@@ -133,7 +134,7 @@ public class ConfigManagerDialog extends GridDialog {
}
} else if (parentElement instanceof LayoutDevice) {
LayoutDevice device = (LayoutDevice)parentElement;
- return device.getConfigs().entrySet().toArray();
+ return device.getConfigs().toArray();
}
return null;
@@ -182,8 +183,7 @@ public class ConfigManagerDialog extends GridDialog {
/**
* Label provider for the {@link TreeViewer}.
- * Supported elements are {@link DeviceType}, {@link LayoutDevice}, and {@link Entry} (where
- * the key is a {@link String} object, and the value is a {@link FolderConfiguration} object).
+ * Supported elements are {@link DeviceType}, {@link LayoutDevice}, and {@link DeviceConfig}.
*
*/
private final static class DeviceLabelProvider implements ITableLabelProvider {
@@ -197,11 +197,11 @@ public class ConfigManagerDialog extends GridDialog {
if (columnIndex == 0) {
return ((LayoutDevice)element).getName();
}
- } else if (element instanceof Entry<?, ?>) {
+ } else if (element instanceof DeviceConfig) {
if (columnIndex == 0) {
- return (String)((Entry<?,?>)element).getKey();
+ return ((DeviceConfig)element).getName();
} else {
- return ((Entry<?,?>)element).getValue().toString();
+ return ((DeviceConfig)element).getConfig().toString();
}
}
return null;
@@ -386,18 +386,18 @@ public class ConfigManagerDialog extends GridDialog {
// are we copying the full device?
if (selection.entry == null) {
// get the config from the origin device
- Map<String, FolderConfiguration> configs = selection.device.getConfigs();
+ List<DeviceConfig> configs = selection.device.getConfigs();
// and copy them in the target device
- for (Entry<String, FolderConfiguration> entry : configs.entrySet()) {
+ for (DeviceConfig config : configs) {
// we need to make a copy of the config object, or it could be modified
// in default/addon by editing the version in the new device.
FolderConfiguration copy = new FolderConfiguration();
- copy.set(entry.getValue());
+ copy.set(config.getConfig());
// the name can stay the same since we are copying a full device
// and the target device has its own new name.
- mManager.addUserConfiguration(targetDevice, entry.getKey(), copy);
+ mManager.addUserConfiguration(targetDevice, config.getName(), copy);
}
} else {
// only copy the config. target device is not the same as the selection, don't
@@ -533,12 +533,12 @@ public class ConfigManagerDialog extends GridDialog {
// this is the easy case. no config to select
path = new Object[] { DeviceType.CUSTOM, device };
} else {
- // this is more complex. we have the configName, but the tree contains Entry<?,?>
+ // this is more complex. we have the configName, but the tree contains DeviceConfig
// Look for the entry.
- Entry<?, ?> match = null;
- for (Entry<?, ?> entry : device.getConfigs().entrySet()) {
- if (entry.getKey().equals(configName)) {
- match = entry;
+ DeviceConfig match = null;
+ for (DeviceConfig config : device.getConfigs()) {
+ if (config.getName().equals(configName)) {
+ match = config;
break;
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
index c267422..ef426df 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -39,6 +39,7 @@ import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice.DeviceConfig;
import com.android.layoutlib.api.IResourceValue;
import com.android.layoutlib.api.IStyleResourceValue;
import com.android.sdklib.IAndroidTarget;
@@ -66,13 +67,12 @@ import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.SortedSet;
-import java.util.Map.Entry;
/**
* A composite that displays the current configuration displayed in a Graphical Layout Editor.
@@ -222,7 +222,7 @@ public class ConfigurationComposite extends Composite {
for (LayoutDevice d : mDeviceList) {
if (d.getName().equals(values[0])) {
device = d;
- FolderConfiguration config = device.getConfigs().get(values[1]);
+ FolderConfiguration config = device.getFolderConfigByName(values[1]);
if (config != null) {
configName = values[1];
@@ -731,9 +731,8 @@ public class ConfigurationComposite extends Composite {
FolderConfiguration testConfig = new FolderConfiguration();
mainloop: for (LayoutDevice device : mDeviceList) {
- for (Entry<String, FolderConfiguration> entry :
- device.getConfigs().entrySet()) {
- testConfig.set(entry.getValue());
+ for (DeviceConfig config : device.getConfigs()) {
+ testConfig.set(config.getConfig());
// look on the locales.
for (int i = 0 ; i < mLocaleList.size() ; i++) {
@@ -748,14 +747,14 @@ public class ConfigurationComposite extends Composite {
// where the edited file is a best config.
if (anyDeviceMatch == null) {
anyDeviceMatch = device;
- anyConfigMatchName = entry.getKey();
+ anyConfigMatchName = config.getName();
anyLocaleIndex = i;
}
if (isCurrentFileBestMatchFor(testConfig)) {
// this is what we want.
bestDeviceMatch = device;
- bestConfigMatchName = entry.getKey();
+ bestConfigMatchName = config.getName();
bestLocaleIndex = i;
break mainloop;
}
@@ -823,7 +822,7 @@ public class ConfigurationComposite extends Composite {
int configIndex = mDeviceConfigCombo.getSelectionIndex();
if (configIndex != -1) {
String configName = mDeviceConfigCombo.getItem(configIndex);
- FolderConfiguration currentConfig = mState.device.getConfigs().get(configName);
+ FolderConfiguration currentConfig = mState.device.getFolderConfigByName(configName);
if (mEditedConfig.isMatchFor(currentConfig)) {
currentConfigIsCompatible = true; // current config is compatible
if (needBestMatch == false || isCurrentFileBestMatchFor(currentConfig)) {
@@ -840,9 +839,8 @@ public class ConfigurationComposite extends Composite {
// first look in the current device.
String matchName = null;
int localeIndex = -1;
- Map<String, FolderConfiguration> configs = mState.device.getConfigs();
- mainloop: for (Entry<String, FolderConfiguration> entry : configs.entrySet()) {
- testConfig.set(entry.getValue());
+ mainloop: for (DeviceConfig config : mState.device.getConfigs()) {
+ testConfig.set(config.getConfig());
// loop on the locales.
for (int i = 0 ; i < mLocaleList.size() ; i++) {
@@ -854,7 +852,7 @@ public class ConfigurationComposite extends Composite {
if (mEditedConfig.isMatchFor(testConfig) &&
isCurrentFileBestMatchFor(testConfig)) {
- matchName = entry.getKey();
+ matchName = config.getName();
localeIndex = i;
break mainloop;
}
@@ -1303,13 +1301,12 @@ public class ConfigurationComposite extends Composite {
mDeviceCombo.select(0);
if (mDeviceList.size() > 0) {
- Map<String, FolderConfiguration> configs = mDeviceList.get(0).getConfigs();
- Set<String> configNames = configs.keySet();
- for (String name : configNames) {
- mDeviceConfigCombo.add(name);
+ List<DeviceConfig> configs = mDeviceList.get(0).getConfigs();
+ for (DeviceConfig config : configs) {
+ mDeviceConfigCombo.add(config.getName());
}
mDeviceConfigCombo.select(0);
- if (configNames.size() == 1) {
+ if (configs.size() == 1) {
mDeviceConfigCombo.setEnabled(false);
}
}
@@ -1377,7 +1374,7 @@ public class ConfigurationComposite extends Composite {
if (mState.device != null) {
int index = mDeviceConfigCombo.getSelectionIndex();
if (index != -1) {
- FolderConfiguration oldConfig = mState.device.getConfigs().get(
+ FolderConfiguration oldConfig = mState.device.getFolderConfigByName(
mDeviceConfigCombo.getItem(index));
LayoutDevice newDevice = mDeviceList.get(deviceIndex);
@@ -1449,40 +1446,37 @@ public class ConfigurationComposite extends Composite {
* @return the name of the closest config match, or possibly null if no configs are compatible
* (this can only happen if the configs don't have a single qualifier that is the same).
*/
- private String getClosestMatch(FolderConfiguration oldConfig,
- Map<String, FolderConfiguration> configs) {
+ private String getClosestMatch(FolderConfiguration oldConfig, List<DeviceConfig> configs) {
// create 2 lists as we're going to go through one and put the candidates in the other.
- ArrayList<Entry<String, FolderConfiguration>> list1 =
- new ArrayList<Entry<String,FolderConfiguration>>();
- ArrayList<Entry<String, FolderConfiguration>> list2 =
- new ArrayList<Entry<String,FolderConfiguration>>();
+ ArrayList<DeviceConfig> list1 = new ArrayList<DeviceConfig>();
+ ArrayList<DeviceConfig> list2 = new ArrayList<DeviceConfig>();
- list1.addAll(configs.entrySet());
+ list1.addAll(configs);
final int count = FolderConfiguration.getQualifierCount();
for (int i = 0 ; i < count ; i++) {
// compute the new candidate list by only taking configs that have
// the same i-th qualifier as the old config
- for (Entry<String, FolderConfiguration> entry : list1) {
+ for (DeviceConfig c : list1) {
ResourceQualifier oldQualifier = oldConfig.getQualifier(i);
- FolderConfiguration config = entry.getValue();
- ResourceQualifier newQualifier = config.getQualifier(i);
+ FolderConfiguration folderConfig = c.getConfig();
+ ResourceQualifier newQualifier = folderConfig.getQualifier(i);
if (oldQualifier == null) {
if (newQualifier == null) {
- list2.add(entry);
+ list2.add(c);
}
} else if (oldQualifier.equals(newQualifier)) {
- list2.add(entry);
+ list2.add(c);
}
}
// at any moment if the new candidate list contains only one match, its name
// is returned.
if (list2.size() == 1) {
- return list2.get(0).getKey();
+ return list2.get(0).getName();
}
// if the list is empty, then all the new configs failed. It is considered ok, and
@@ -1500,7 +1494,7 @@ public class ConfigurationComposite extends Composite {
// (if there are more than one, then there's a duplicate config and it doesn't matter,
// we take the first one).
if (list1.size() > 0) {
- return list1.get(0).getKey();
+ return list1.get(0).getName();
}
return null;
@@ -1514,22 +1508,20 @@ public class ConfigurationComposite extends Composite {
mDeviceConfigCombo.removeAll();
if (mState.device != null) {
- Set<String> configNames = mState.device.getConfigs().keySet();
-
int selectionIndex = 0;
int i = 0;
- for (String name : configNames) {
- mDeviceConfigCombo.add(name);
+ for (DeviceConfig config : mState.device.getConfigs()) {
+ mDeviceConfigCombo.add(config.getName());
- if (name.equals(refName)) {
+ if (config.getName().equals(refName)) {
selectionIndex = i;
}
i++;
}
mDeviceConfigCombo.select(selectionIndex);
- mDeviceConfigCombo.setEnabled(configNames.size() > 1);
+ mDeviceConfigCombo.setEnabled(mState.device.getConfigs().size() > 1);
}
}
@@ -1588,7 +1580,7 @@ public class ConfigurationComposite extends Composite {
// get the device config from the device/config combos.
int configIndex = mDeviceConfigCombo.getSelectionIndex();
String name = mDeviceConfigCombo.getItem(configIndex);
- FolderConfiguration config = mState.device.getConfigs().get(name);
+ FolderConfiguration config = mState.device.getFolderConfigByName(name);
// replace the config with the one from the device
mCurrentConfig.set(config);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
index dd0fb74..4f7b7f2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
@@ -33,10 +33,9 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreen
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
/**
* Class representing a layout device.
@@ -58,10 +57,35 @@ public class LayoutDevice {
private final String mName;
- /** editable map of the config */
- private Map<String, FolderConfiguration> mEditMap = new HashMap<String, FolderConfiguration>();
- /** unmodifiable map returned by {@link #getConfigs()}. */
- private Map<String, FolderConfiguration> mMap;
+ /**
+ * Wrapper around a {@link FolderConfiguration}.
+ * <p/>This adds a name, accessible through {@link #getName()}.
+ * <p/>The folder config can be accessed through {@link #getConfig()}.
+ *
+ */
+ public final static class DeviceConfig {
+ private final String mName;
+ private final FolderConfiguration mConfig;
+
+ DeviceConfig(String name, FolderConfiguration config) {
+ mName = name;
+ mConfig = config;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public FolderConfiguration getConfig() {
+ return mConfig;
+ }
+ }
+
+ /** editable list of the config */
+ private final ArrayList<DeviceConfig> mConfigs = new ArrayList<DeviceConfig>();
+ /** Read-only list */
+ private List<DeviceConfig> mROList;
+
private float mXDpi = Float.NaN;
private float mYDpi = Float.NaN;
@@ -93,8 +117,10 @@ public class LayoutDevice {
}
// then save all the configs.
- for (Entry<String, FolderConfiguration> entry : mEditMap.entrySet()) {
- saveConfigTo(doc, deviceNode, entry.getKey(), entry.getValue());
+ synchronized (mConfigs) {
+ for (DeviceConfig config : mConfigs) {
+ saveConfigTo(doc, deviceNode, config.getName(), config.getConfig());
+ }
}
}
@@ -207,33 +233,91 @@ public class LayoutDevice {
}
}
+ /**
+ * Adds config to the LayoutDevice.
+ * <p/>This ensures that no two configurations have the same. If a config already exists
+ * with the same name, the new config replaces it.
+ *
+ * @param name the name of the config.
+ * @param config the config.
+ */
void addConfig(String name, FolderConfiguration config) {
- mEditMap.put(name, config);
- _seal();
+ synchronized (mConfigs) {
+ doAddConfig(name, config);
+ seal();
+ }
}
- void addConfigs(Map<String, FolderConfiguration> configs) {
- mEditMap.putAll(configs);
- _seal();
+ /**
+ * Adds a list of config to the LayoutDevice
+ * <p/>This ensures that no two configurations have the same. If a config already exists
+ * with the same name, the new config replaces it.
+
+ * @param configs the configs to add.
+ */
+ void addConfigs(List<DeviceConfig> configs) {
+ synchronized (mConfigs) {
+ // add the configs manually one by one, to check for no duplicate.
+ for (DeviceConfig config : configs) {
+ String name = config.getName();
+
+ for (DeviceConfig c : mConfigs) {
+ if (c.getName().equals(name)) {
+ mConfigs.remove(c);
+ break;
+ }
+ }
+
+ mConfigs.add(config);
+ }
+
+ seal();
+ }
}
+ /**
+ * Removes a config by its name.
+ * @param name the name of the config to remove.
+ */
void removeConfig(String name) {
- mEditMap.remove(name);
- _seal();
+ synchronized (mConfigs) {
+ for (DeviceConfig config : mConfigs) {
+ if (config.getName().equals(name)) {
+ mConfigs.remove(config);
+ seal();
+ return;
+ }
+ }
+ }
}
/**
- * Adds config to the LayoutDevice. This is to be used to add plenty of configurations.
- * It must be followed by {@link #_seal()}.
+ * Adds config to the LayoutDevice. This is to be used to add plenty of
+ * configurations. It must be followed by {@link #_seal()}.
+ * <p/>This ensures that no two configurations have the same. If a config already exists
+ * with the same name, the new config replaces it.
+ * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong>
+ *
* @param name the name of the config
* @param config the config.
*/
- void _addConfig(String name, FolderConfiguration config) {
- mEditMap.put(name, config);
+ private void doAddConfig(String name, FolderConfiguration config) {
+ // remove config that would have the same name to ensure no duplicate
+ for (DeviceConfig c : mConfigs) {
+ if (c.getName().equals(name)) {
+ mConfigs.remove(c);
+ break;
+ }
+ }
+ mConfigs.add(new DeviceConfig(name, config));
}
- void _seal() {
- mMap = Collections.unmodifiableMap(mEditMap);
+ /**
+ * Seals the layout device by setting up {@link #mROList}.
+ * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong>
+ */
+ private void seal() {
+ mROList = Collections.unmodifiableList(mConfigs);
}
void setXDpi(float xdpi) {
@@ -248,8 +332,43 @@ public class LayoutDevice {
return mName;
}
- public Map<String, FolderConfiguration> getConfigs() {
- return mMap;
+ /**
+ * Returns an unmodifiable list of all the {@link DeviceConfig}.
+ */
+ public List<DeviceConfig> getConfigs() {
+ synchronized (mConfigs) {
+ return mROList;
+ }
+ }
+
+ /**
+ * Returns a {@link DeviceConfig} by its name.
+ */
+ public DeviceConfig getDeviceConfigByName(String name) {
+ synchronized (mConfigs) {
+ for (DeviceConfig config : mConfigs) {
+ if (config.getName().equals(name)) {
+ return config;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link FolderConfiguration} by its name.
+ */
+ public FolderConfiguration getFolderConfigByName(String name) {
+ synchronized (mConfigs) {
+ for (DeviceConfig config : mConfigs) {
+ if (config.getName().equals(name)) {
+ return config.getConfig();
+ }
+ }
+ }
+
+ return null;
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
index 3b19f17..60c8d95 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
@@ -18,6 +18,7 @@ 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.ide.eclipse.adt.internal.sdk.LayoutDevice.DeviceConfig;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.SdkConstants;
@@ -37,7 +38,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -180,7 +180,7 @@ public class LayoutDeviceManager {
newDevice.setYDpi(newYDpi);
// and get the Folderconfiguration
- Map<String, FolderConfiguration> configs = device.getConfigs();
+ List<DeviceConfig> configs = device.getConfigs();
newDevice.addConfigs(configs);
// replace the old device with the new
@@ -191,7 +191,6 @@ public class LayoutDeviceManager {
return newDevice;
}
-
/**
* Adds or replaces a configuration in a given {@link LayoutDevice}.
* @param device the device to modify