aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--androidprefs/src/com/android/prefs/AndroidLocation.java6
-rw-r--r--apkbuilder/src/com/android/apkbuilder/ApkBuilder.java2
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java346
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Device.java8
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java8
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java6
-rwxr-xr-xddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java8
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java2
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java6
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java55
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java422
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java379
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java338
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java219
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java1684
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java21
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java78
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java140
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java48
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java6
-rw-r--r--scripts/app_engine_server/LICENSE202
-rwxr-xr-xscripts/app_engine_server/app.yaml16
-rw-r--r--scripts/app_engine_server/gae_shell/README17
-rw-r--r--scripts/app_engine_server/gae_shell/__init__.py0
-rw-r--r--scripts/app_engine_server/gae_shell/__init__.pycbin0 -> 216 bytes
-rwxr-xr-xscripts/app_engine_server/gae_shell/shell.py308
-rwxr-xr-xscripts/app_engine_server/gae_shell/shell.py~308
-rw-r--r--scripts/app_engine_server/gae_shell/static/shell.js195
-rw-r--r--scripts/app_engine_server/gae_shell/static/spinner.gifbin0 -> 1514 bytes
-rw-r--r--scripts/app_engine_server/gae_shell/templates/shell.html122
-rw-r--r--scripts/app_engine_server/index.yaml12
-rw-r--r--scripts/app_engine_server/memcache_zipserve.py412
-rwxr-xr-xscripts/combine_sdks.sh93
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java431
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/Main.java147
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java238
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java38
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java66
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java37
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java)160
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/avd/HardwareProperties.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java)2
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java (renamed from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java)70
54 files changed, 4616 insertions, 2260 deletions
diff --git a/androidprefs/src/com/android/prefs/AndroidLocation.java b/androidprefs/src/com/android/prefs/AndroidLocation.java
index a7ceb76..68a87b7 100644
--- a/androidprefs/src/com/android/prefs/AndroidLocation.java
+++ b/androidprefs/src/com/android/prefs/AndroidLocation.java
@@ -32,9 +32,9 @@ public final class AndroidLocation {
private static final String ANDROID_SDK_VERSION = "SDK-1.0";
/**
- * VM folder inside the path returned by {@link #getFolder()}
+ * Virtual Device folder inside the path returned by {@link #getFolder()}
*/
- public static final String FOLDER_VMS = "vm";
+ public static final String FOLDER_AVD = "avd";
/**
* Throw when the location of the android folder couldn't be found.
@@ -56,7 +56,7 @@ public final class AndroidLocation {
*/
public final static String getFolder() throws AndroidLocationException {
if (sPrefsLocation == null) {
- String home = findValidPath("user.home", "HOME");
+ String home = findValidPath("ANDROID_SDK_HOME", "user.home", "HOME");
// if the above failed, we throw an exception.
if (home == null) {
diff --git a/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
index 9a4d24b..8616cda 100644
--- a/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
+++ b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
@@ -319,7 +319,7 @@ public final class ApkBuilder {
* @param files
* @param javaResources
* @param storeType the optional type of the debug keystore. If <code>null</code>, the default
- * keystore type of the VM is used.
+ * keystore type of the Java VM is used.
*/
private void createPackage(File outFile, ArrayList<FileInputStream> zipArchives,
ArrayList<File> files, ArrayList<ApkFile> javaResources,
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
index 154bfa1..42022fe 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/AdbHelper.java
@@ -268,55 +268,60 @@ final class AdbHelper {
};
byte[] reply;
- SocketChannel adbChan = SocketChannel.open(adbSockAddr);
- adbChan.configureBlocking(false);
-
- // if the device is not -1, then we first tell adb we're looking to talk
- // to a specific device
- setDevice(adbChan, device);
-
- if (write(adbChan, request) == false)
- throw new IOException("failed asking for frame buffer");
-
- AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
- + resp.message);
- adbChan.close();
- return null;
- }
-
- reply = new byte[16];
- if (read(adbChan, reply) == false) {
- Log.w("ddms", "got partial reply from ADB fb:");
- Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
- adbChan.close();
- return null;
- }
- ByteBuffer buf = ByteBuffer.wrap(reply);
- buf.order(ByteOrder.LITTLE_ENDIAN);
-
- imageParams.bpp = buf.getInt();
- imageParams.size = buf.getInt();
- imageParams.width = buf.getInt();
- imageParams.height = buf.getInt();
-
- Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
- + imageParams.size + ", width=" + imageParams.width
- + ", height=" + imageParams.height);
-
- if (write(adbChan, nudge) == false)
- throw new IOException("failed nudging");
-
- reply = new byte[imageParams.size];
- if (read(adbChan, reply) == false) {
- Log.w("ddms", "got truncated reply from ADB fb data");
- adbChan.close();
- return null;
+ SocketChannel adbChan = null;
+ try {
+ adbChan = SocketChannel.open(adbSockAddr);
+ adbChan.configureBlocking(false);
+
+ // if the device is not -1, then we first tell adb we're looking to talk
+ // to a specific device
+ setDevice(adbChan, device);
+
+ if (write(adbChan, request) == false)
+ throw new IOException("failed asking for frame buffer");
+
+ AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
+ if (!resp.ioSuccess || !resp.okay) {
+ Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
+ + resp.message);
+ adbChan.close();
+ return null;
+ }
+
+ reply = new byte[16];
+ if (read(adbChan, reply) == false) {
+ Log.w("ddms", "got partial reply from ADB fb:");
+ Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
+ adbChan.close();
+ return null;
+ }
+ ByteBuffer buf = ByteBuffer.wrap(reply);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ imageParams.bpp = buf.getInt();
+ imageParams.size = buf.getInt();
+ imageParams.width = buf.getInt();
+ imageParams.height = buf.getInt();
+
+ Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
+ + imageParams.size + ", width=" + imageParams.width
+ + ", height=" + imageParams.height);
+
+ if (write(adbChan, nudge) == false)
+ throw new IOException("failed nudging");
+
+ reply = new byte[imageParams.size];
+ if (read(adbChan, reply) == false) {
+ Log.w("ddms", "got truncated reply from ADB fb data");
+ adbChan.close();
+ return null;
+ }
+ imageParams.data = reply;
+ } finally {
+ if (adbChan != null) {
+ adbChan.close();
+ }
}
- imageParams.data = reply;
-
- adbChan.close();
return imageParams;
}
@@ -330,58 +335,61 @@ final class AdbHelper {
throws IOException {
Log.v("ddms", "execute: running " + command);
- SocketChannel adbChan = SocketChannel.open(adbSockAddr);
- adbChan.configureBlocking(false);
-
- // if the device is not -1, then we first tell adb we're looking to talk
- // to a specific device
- setDevice(adbChan, device);
-
- byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
- if (write(adbChan, request) == false)
- throw new IOException("failed submitting shell command");
+ SocketChannel adbChan = null;
+ try {
+ adbChan = SocketChannel.open(adbSockAddr);
+ adbChan.configureBlocking(false);
- AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- Log.e("ddms", "ADB rejected shell command (" + command + "): "
- + resp.message);
- throw new IOException("sad result from adb: " + resp.message);
- }
+ // if the device is not -1, then we first tell adb we're looking to
+ // talk
+ // to a specific device
+ setDevice(adbChan, device);
- byte[] data = new byte[16384];
- ByteBuffer buf = ByteBuffer.wrap(data);
- while (true) {
- int count;
+ byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
+ if (write(adbChan, request) == false)
+ throw new IOException("failed submitting shell command");
- if (rcvr != null && rcvr.isCancelled()) {
- Log.v("ddms", "execute: cancelled");
- break;
+ AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
+ if (!resp.ioSuccess || !resp.okay) {
+ Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);
+ throw new IOException("sad result from adb: " + resp.message);
}
- count = adbChan.read(buf);
- if (count < 0) {
- // we're at the end, we flush the output
- rcvr.flush();
- Log.v("ddms",
- "execute '" + command + "' on '" + device + "' : EOF hit. Read: " + count);
- break;
- } else if (count == 0) {
- try {
- Thread.sleep(WAIT_TIME * 5);
- } catch (InterruptedException ie) {
+ byte[] data = new byte[16384];
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ while (true) {
+ int count;
+
+ if (rcvr != null && rcvr.isCancelled()) {
+ Log.v("ddms", "execute: cancelled");
+ break;
}
- } else {
- if (rcvr != null) {
- rcvr.addOutput(buf.array(), buf.arrayOffset(), buf
- .position());
+
+ count = adbChan.read(buf);
+ if (count < 0) {
+ // we're at the end, we flush the output
+ rcvr.flush();
+ Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: "
+ + count);
+ break;
+ } else if (count == 0) {
+ try {
+ Thread.sleep(WAIT_TIME * 5);
+ } catch (InterruptedException ie) {
+ }
+ } else {
+ if (rcvr != null) {
+ rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());
+ }
+ buf.rewind();
}
- buf.rewind();
}
+ } finally {
+ if (adbChan != null) {
+ adbChan.close();
+ }
+ Log.v("ddms", "execute: returning");
}
-
- adbChan.close();
-
- Log.v("ddms", "execute: returning");
}
/**
@@ -407,49 +415,55 @@ final class AdbHelper {
*/
public static void runLogService(InetSocketAddress adbSockAddr, Device device, String logName,
LogReceiver rcvr) throws IOException {
- SocketChannel adbChan = SocketChannel.open(adbSockAddr);
- adbChan.configureBlocking(false);
-
- // if the device is not -1, then we first tell adb we're looking to talk
- // to a specific device
- setDevice(adbChan, device);
-
- byte[] request = formAdbRequest("log:" + logName);
- if (write(adbChan, request) == false) {
- throw new IOException("failed to submit the log command");
- }
-
- AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected log command: " + resp.message);
- }
-
- byte[] data = new byte[16384];
- ByteBuffer buf = ByteBuffer.wrap(data);
- while (true) {
- int count;
-
- if (rcvr != null && rcvr.isCancelled()) {
- break;
+ SocketChannel adbChan = null;
+
+ try {
+ adbChan = SocketChannel.open(adbSockAddr);
+ adbChan.configureBlocking(false);
+
+ // if the device is not -1, then we first tell adb we're looking to talk
+ // to a specific device
+ setDevice(adbChan, device);
+
+ byte[] request = formAdbRequest("log:" + logName);
+ if (write(adbChan, request) == false) {
+ throw new IOException("failed to submit the log command");
}
-
- count = adbChan.read(buf);
- if (count < 0) {
- break;
- } else if (count == 0) {
- try {
- Thread.sleep(WAIT_TIME * 5);
- } catch (InterruptedException ie) {
+
+ AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
+ if (!resp.ioSuccess || !resp.okay) {
+ throw new IOException("Device rejected log command: " + resp.message);
+ }
+
+ byte[] data = new byte[16384];
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ while (true) {
+ int count;
+
+ if (rcvr != null && rcvr.isCancelled()) {
+ break;
}
- } else {
- if (rcvr != null) {
- rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position());
+
+ count = adbChan.read(buf);
+ if (count < 0) {
+ break;
+ } else if (count == 0) {
+ try {
+ Thread.sleep(WAIT_TIME * 5);
+ } catch (InterruptedException ie) {
+ }
+ } else {
+ if (rcvr != null) {
+ rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position());
+ }
+ buf.rewind();
}
- buf.rewind();
+ }
+ } finally {
+ if (adbChan != null) {
+ adbChan.close();
}
}
-
- adbChan.close();
}
/**
@@ -464,24 +478,29 @@ final class AdbHelper {
public static boolean createForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
- SocketChannel adbChan = SocketChannel.open(adbSockAddr);
- adbChan.configureBlocking(false);
-
- byte[] request = formAdbRequest(String.format(
- "host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
- device.serialNumber, localPort, remotePort));
-
- if (write(adbChan, request) == false) {
- throw new IOException("failed to submit the forward command.");
- }
-
- AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected command: " + resp.message);
+ SocketChannel adbChan = null;
+ try {
+ adbChan = SocketChannel.open(adbSockAddr);
+ adbChan.configureBlocking(false);
+
+ byte[] request = formAdbRequest(String.format(
+ "host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
+ device.serialNumber, localPort, remotePort));
+
+ if (write(adbChan, request) == false) {
+ throw new IOException("failed to submit the forward command.");
+ }
+
+ AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
+ if (!resp.ioSuccess || !resp.okay) {
+ throw new IOException("Device rejected command: " + resp.message);
+ }
+ } finally {
+ if (adbChan != null) {
+ adbChan.close();
+ }
}
- adbChan.close();
-
return true;
}
@@ -497,24 +516,29 @@ final class AdbHelper {
public static boolean removeForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
- SocketChannel adbChan = SocketChannel.open(adbSockAddr);
- adbChan.configureBlocking(false);
-
- byte[] request = formAdbRequest(String.format(
- "host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
- device.serialNumber, localPort, remotePort));
-
- if (!write(adbChan, request)) {
- throw new IOException("failed to submit the remove forward command.");
- }
-
- AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
- if (!resp.ioSuccess || !resp.okay) {
- throw new IOException("Device rejected command: " + resp.message);
+ SocketChannel adbChan = null;
+ try {
+ adbChan = SocketChannel.open(adbSockAddr);
+ adbChan.configureBlocking(false);
+
+ byte[] request = formAdbRequest(String.format(
+ "host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
+ device.serialNumber, localPort, remotePort));
+
+ if (!write(adbChan, request)) {
+ throw new IOException("failed to submit the remove forward command.");
+ }
+
+ AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
+ if (!resp.ioSuccess || !resp.okay) {
+ throw new IOException("Device rejected command: " + resp.message);
+ }
+ } finally {
+ if (adbChan != null) {
+ adbChan.close();
+ }
}
- adbChan.close();
-
return true;
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
index 8291f59..34ef432 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
@@ -69,8 +69,8 @@ public final class Device implements IDevice {
/** Serial number of the device */
String serialNumber = null;
- /** Name of the vm */
- String mVmName = null;
+ /** Name of the AVD */
+ String mAvdName = null;
/** State of the device. */
DeviceState state = null;
@@ -94,8 +94,8 @@ public final class Device implements IDevice {
return serialNumber;
}
- public String getVmName() {
- return mVmName;
+ public String getAvdName() {
+ return mAvdName;
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
index 8547ac1..f9d0fa0 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
@@ -420,11 +420,11 @@ final class DeviceMonitor {
device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
new GetPropReceiver(device));
- // now get the emulator VM name (if applicable).
+ // now get the emulator Virtual Device name (if applicable).
if (device.isEmulator()) {
EmulatorConsole console = EmulatorConsole.getConsole(device);
if (console != null) {
- device.mVmName = console.getVmName();
+ device.mAvdName = console.getAvdName();
}
}
} catch (IOException e) {
@@ -470,7 +470,7 @@ final class DeviceMonitor {
} catch (IOException e1) {
// we can ignore that one. It may already have been closed.
}
- Log.e("DeviceMonitor",
+ Log.d("DeviceMonitor",
"Connection Failure when starting to monitor device '"
+ device + "' : " + e.getMessage());
}
@@ -558,7 +558,7 @@ final class DeviceMonitor {
processIncomingJdwpData(device, socket, length);
} catch (IOException ioe) {
- Log.e("DeviceMonitor",
+ Log.d("DeviceMonitor",
"Error reading jdwp list: " + ioe.getMessage());
socket.close();
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
index e00073c..74c432d 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
@@ -54,7 +54,7 @@ public final class EmulatorConsole {
private final static String HOST = "127.0.0.1"; //$NON-NLS-1$
private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$
- private final static String COMMAND_VM_NAME = "vm name\r\n"; //$NON-NLS-1$
+ private final static String COMMAND_AVD_NAME = "vm name\r\n"; //$NON-NLS-1$ // TODO change with emulator
private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$
@@ -309,8 +309,8 @@ public final class EmulatorConsole {
}
}
- public synchronized String getVmName() {
- if (sendCommand(COMMAND_VM_NAME)) {
+ public synchronized String getAvdName() {
+ if (sendCommand(COMMAND_AVD_NAME)) {
String[] result = readLines();
if (result != null && result.length == 2) { // this should be the name on first line,
// and ok on 2nd line
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index 61d1ca4..664b0c9 100755
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -46,13 +46,13 @@ public interface IDevice {
public String getSerialNumber();
/**
- * Returns the name of the VM the emulator is running.
+ * Returns the name of the AVD the emulator is running.
* <p/>This is only valid if {@link #isEmulator()} returns true.
- * <p/>If the emulator is not running any VM (for instance it's running from an Android source
+ * <p/>If the emulator is not running any AVD (for instance it's running from an Android source
* tree build), this method will return "<code>&lt;build&gt;</code>".
- * @return the name of the VM or <code>null</code> if there isn't any.
+ * @return the name of the AVD or <code>null</code> if there isn't any.
*/
- public String getVmName();
+ public String getAvdName();
/**
* Returns the state of the device.
diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index 556fc9b..54ffae8 100644
--- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -201,7 +201,7 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
- public String getVmName() {
+ public String getAvdName() {
return "";
}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
index 1331a09..fa3f0e4 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
@@ -200,15 +200,15 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
case DEVICE_COL_STATE:
return getStateString(device);
case DEVICE_COL_BUILD: {
- String vmName = device.getVmName();
+ String avdName = device.getAvdName();
String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
String version = device.getProperty(Device.PROP_BUILD_VERSION);
if (device.isEmulator()) {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
- return String.format("%1$s [%2$s, debug]", vmName, //$NON-NLS-1$
+ return String.format("%1$s [%2$s, debug]", avdName, //$NON-NLS-1$
version);
} else {
- return String.format("%1$s [%2$s]", vmName, version); //$NON-NLS-1$
+ return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$
}
} else {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java
new file mode 100644
index 0000000..473387a
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 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.ddmuilib.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+
+import java.util.ArrayList;
+
+public class DisplayFilteredLog extends DisplayLog {
+
+ public DisplayFilteredLog(String name) {
+ super(name);
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ ArrayList<ValueDisplayDescriptor> valueDescriptors =
+ new ArrayList<ValueDisplayDescriptor>();
+
+ ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
+ new ArrayList<OccurrenceDisplayDescriptor>();
+
+ if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
+ addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_FILTERED_LOG;
+ }
+}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java
new file mode 100644
index 0000000..0cffd7e
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2008 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.ddmuilib.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.EventValueDescription;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYAreaRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.time.Millisecond;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DisplayGraph extends EventDisplay {
+
+ public DisplayGraph(String name) {
+ super(name);
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ Collection<TimeSeriesCollection> datasets = mValueTypeDataSetMap.values();
+ for (TimeSeriesCollection dataset : datasets) {
+ dataset.removeAllSeries();
+ }
+ if (mOccurrenceDataSet != null) {
+ mOccurrenceDataSet.removeAllSeries();
+ }
+ mValueDescriptorSeriesMap.clear();
+ mOcurrenceDescriptorSeriesMap.clear();
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ String title = getChartTitle(logParser);
+ return createCompositeChart(parent, logParser, title);
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ ArrayList<ValueDisplayDescriptor> valueDescriptors =
+ new ArrayList<ValueDisplayDescriptor>();
+
+ ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
+ new ArrayList<OccurrenceDisplayDescriptor>();
+
+ if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
+ updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
+ }
+ }
+
+ /**
+ * Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
+ * by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
+ * the two lists.
+ * <p/>This method is only called when at least one of the descriptor list is non empty.
+ * @param event
+ * @param logParser
+ * @param valueDescriptors
+ * @param occurrenceDescriptors
+ */
+ private void updateChart(EventContainer event, EventLogParser logParser,
+ ArrayList<ValueDisplayDescriptor> valueDescriptors,
+ ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
+ Map<Integer, String> tagMap = logParser.getTagMap();
+
+ Millisecond millisecondTime = null;
+ long msec = -1;
+
+ // If the event container is a cpu container (tag == 2721), and there is no descriptor
+ // for the total CPU load, then we do accumulate all the values.
+ boolean accumulateValues = false;
+ double accumulatedValue = 0;
+
+ if (event.mTag == 2721) {
+ accumulateValues = true;
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ accumulateValues &= (descriptor.valueIndex != 0);
+ }
+ }
+
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ try {
+ // get the hashmap for this descriptor
+ HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descriptor);
+
+ // if it's not there yet, we create it.
+ if (map == null) {
+ map = new HashMap<Integer, TimeSeries>();
+ mValueDescriptorSeriesMap.put(descriptor, map);
+ }
+
+ // get the TimeSeries for this pid
+ TimeSeries timeSeries = map.get(event.pid);
+
+ // if it doesn't exist yet, we create it
+ if (timeSeries == null) {
+ // get the series name
+ String seriesFullName = null;
+ String seriesLabel = getSeriesLabel(event, descriptor);
+
+ switch (mValueDescriptorCheck) {
+ case EVENT_CHECK_SAME_TAG:
+ seriesFullName = String.format("%1$s / %2$s", seriesLabel,
+ descriptor.valueName);
+ break;
+ case EVENT_CHECK_SAME_VALUE:
+ seriesFullName = String.format("%1$s", seriesLabel);
+ break;
+ default:
+ seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
+ tagMap.get(descriptor.eventTag),
+ descriptor.valueName);
+ break;
+ }
+
+ // get the data set for this ValueType
+ TimeSeriesCollection dataset = getValueDataset(
+ logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
+ .getValueType(),
+ accumulateValues);
+
+ // create the series
+ timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
+ if (mMaximumChartItemAge != -1) {
+ timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
+ }
+
+ dataset.addSeries(timeSeries);
+
+ // add it to the map.
+ map.put(event.pid, timeSeries);
+ }
+
+ // update the timeSeries.
+
+ // get the value from the event
+ double value = event.getValueAsDouble(descriptor.valueIndex);
+
+ // accumulate the values if needed.
+ if (accumulateValues) {
+ accumulatedValue += value;
+ value = accumulatedValue;
+ }
+
+ // get the time
+ if (millisecondTime == null) {
+ msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ millisecondTime = new Millisecond(new Date(msec));
+ }
+
+ // add the value to the time series
+ timeSeries.addOrUpdate(millisecondTime, value);
+ } catch (InvalidTypeException e) {
+ // just ignore this descriptor if there's a type mismatch
+ }
+ }
+
+ for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
+ try {
+ // get the hashmap for this descriptor
+ HashMap<Integer, TimeSeries> map = mOcurrenceDescriptorSeriesMap.get(descriptor);
+
+ // if it's not there yet, we create it.
+ if (map == null) {
+ map = new HashMap<Integer, TimeSeries>();
+ mOcurrenceDescriptorSeriesMap.put(descriptor, map);
+ }
+
+ // get the TimeSeries for this pid
+ TimeSeries timeSeries = map.get(event.pid);
+
+ // if it doesn't exist yet, we create it.
+ if (timeSeries == null) {
+ String seriesLabel = getSeriesLabel(event, descriptor);
+
+ String seriesFullName = String.format("[%1$s:%2$s]",
+ tagMap.get(descriptor.eventTag), seriesLabel);
+
+ timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
+ if (mMaximumChartItemAge != -1) {
+ timeSeries.setMaximumItemAge(mMaximumChartItemAge);
+ }
+
+ getOccurrenceDataSet().addSeries(timeSeries);
+
+ map.put(event.pid, timeSeries);
+ }
+
+ // update the series
+
+ // get the time
+ if (millisecondTime == null) {
+ msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ millisecondTime = new Millisecond(new Date(msec));
+ }
+
+ // add the value to the time series
+ timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
+ } catch (InvalidTypeException e) {
+ // just ignore this descriptor if there's a type mismatch
+ }
+ }
+
+ // go through all the series and remove old values.
+ if (msec != -1 && mMaximumChartItemAge != -1) {
+ Collection<HashMap<Integer, TimeSeries>> pidMapValues =
+ mValueDescriptorSeriesMap.values();
+
+ for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
+ Collection<TimeSeries> seriesCollection = pidMapValue.values();
+
+ for (TimeSeries timeSeries : seriesCollection) {
+ timeSeries.removeAgedItems(msec, true);
+ }
+ }
+
+ pidMapValues = mOcurrenceDescriptorSeriesMap.values();
+ for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
+ Collection<TimeSeries> seriesCollection = pidMapValue.values();
+
+ for (TimeSeries timeSeries : seriesCollection) {
+ timeSeries.removeAgedItems(msec, true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link TimeSeriesCollection} for a specific {@link com.android.ddmlib.log.EventValueDescription.ValueType}.
+ * If the data set is not yet created, it is first allocated and set up into the
+ * {@link org.jfree.chart.JFreeChart} object.
+ * @param type the {@link com.android.ddmlib.log.EventValueDescription.ValueType} of the data set.
+ * @param accumulateValues
+ */
+ private TimeSeriesCollection getValueDataset(EventValueDescription.ValueType type, boolean accumulateValues) {
+ TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
+ if (dataset == null) {
+ // create the data set and store it in the map
+ dataset = new TimeSeriesCollection();
+ mValueTypeDataSetMap.put(type, dataset);
+
+ // create the renderer and configure it depending on the ValueType
+ AbstractXYItemRenderer renderer;
+ if (type == EventValueDescription.ValueType.PERCENT && accumulateValues) {
+ renderer = new XYAreaRenderer();
+ } else {
+ XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
+ r.setBaseShapesVisible(type != EventValueDescription.ValueType.PERCENT);
+
+ renderer = r;
+ }
+
+ // set both the dataset and the renderer in the plot object.
+ XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setDataset(mDataSetCount, dataset);
+ xyPlot.setRenderer(mDataSetCount, renderer);
+
+ // put a new axis label, and configure it.
+ NumberAxis axis = new NumberAxis(type.toString());
+
+ if (type == EventValueDescription.ValueType.PERCENT) {
+ // force percent range to be (0,100) fixed.
+ axis.setAutoRange(false);
+ axis.setRange(0., 100.);
+ }
+
+ // for the index, we ignore the occurrence dataset
+ int count = mDataSetCount;
+ if (mOccurrenceDataSet != null) {
+ count--;
+ }
+
+ xyPlot.setRangeAxis(count, axis);
+ if ((count % 2) == 0) {
+ xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
+ } else {
+ xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
+ }
+
+ // now we link the dataset and the axis
+ xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
+
+ mDataSetCount++;
+ }
+
+ return dataset;
+ }
+
+ /**
+ * Return the series label for this event. This only contains the pid information.
+ * @param event the {@link EventContainer}
+ * @param descriptor the {@link OccurrenceDisplayDescriptor}
+ * @return the series label.
+ * @throws InvalidTypeException
+ */
+ private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
+ throws InvalidTypeException {
+ if (descriptor.seriesValueIndex != -1) {
+ if (descriptor.includePid == false) {
+ return event.getValueAsString(descriptor.seriesValueIndex);
+ } else {
+ return String.format("%1$s (%2$d)",
+ event.getValueAsString(descriptor.seriesValueIndex), event.pid);
+ }
+ }
+
+ return Integer.toString(event.pid);
+ }
+
+ /**
+ * Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
+ * yet created, it is first allocated and set up into the {@link org.jfree.chart.JFreeChart} object.
+ */
+ private TimeSeriesCollection getOccurrenceDataSet() {
+ if (mOccurrenceDataSet == null) {
+ mOccurrenceDataSet = new TimeSeriesCollection();
+
+ XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
+
+ OccurrenceRenderer renderer = new OccurrenceRenderer();
+ renderer.setBaseShapesVisible(false);
+ xyPlot.setRenderer(mDataSetCount, renderer);
+
+ mDataSetCount++;
+ }
+
+ return mOccurrenceDataSet;
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_GRAPH;
+ }
+
+ /**
+ * Sets the current {@link EventLogParser} object.
+ */
+ @Override
+ protected void setNewLogParser(EventLogParser logParser) {
+ if (mChart != null) {
+ mChart.setTitle(getChartTitle(logParser));
+ }
+ }
+ /**
+ * Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
+ *
+ * @param logParser the logParser.
+ * @return the chart title.
+ */
+ private String getChartTitle(EventLogParser logParser) {
+ if (mValueDescriptors.size() > 0) {
+ String chartDesc = null;
+ switch (mValueDescriptorCheck) {
+ case EVENT_CHECK_SAME_TAG:
+ if (logParser != null) {
+ chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
+ }
+ break;
+ case EVENT_CHECK_SAME_VALUE:
+ if (logParser != null) {
+ chartDesc = String.format("%1$s / %2$s",
+ logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
+ mValueDescriptors.get(0).valueName);
+ }
+ break;
+ }
+
+ if (chartDesc != null) {
+ return String.format("%1$s - %2$s", mName, chartDesc);
+ }
+ }
+
+ return mName;
+ }
+} \ No newline at end of file
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java
new file mode 100644
index 0000000..26296f3
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2008 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.ddmuilib.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.EventValueDescription;
+import com.android.ddmlib.log.InvalidTypeException;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ddmuilib.TableHelper;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+public class DisplayLog extends EventDisplay {
+ public DisplayLog(String name) {
+ super(name);
+ }
+
+ private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
+ private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
+ private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
+ private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
+ private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
+ private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ mLogTable.removeAll();
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ addToLog(event, logParser);
+ }
+
+ /**
+ * Creates the UI for the event display.
+ *
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ Control createComposite(Composite parent, EventLogParser logParser, ILogColumnListener listener) {
+ return createLogUI(parent, listener);
+ }
+
+ /**
+ * Adds an {@link EventContainer} to the log.
+ *
+ * @param event the event.
+ * @param logParser the log parser.
+ */
+ private void addToLog(EventContainer event, EventLogParser logParser) {
+ ScrollBar bar = mLogTable.getVerticalBar();
+ boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
+
+ // get the date.
+ Calendar c = Calendar.getInstance();
+ long msec = (long) event.sec * 1000L;
+ c.setTimeInMillis(msec);
+
+ // convert the time into a string
+ String date = String.format("%1$tF %1$tT", c);
+
+ String eventName = logParser.getTagMap().get(event.mTag);
+ String pidName = Integer.toString(event.pid);
+
+ // get the value description
+ EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
+ if (valueDescription != null) {
+ for (int i = 0; i < valueDescription.length; i++) {
+ EventValueDescription description = valueDescription[i];
+ try {
+ String value = event.getValueAsString(i);
+
+ logValue(date, pidName, eventName, description.getName(), value,
+ description.getEventValueType(), description.getValueType());
+ } catch (InvalidTypeException e) {
+ logValue(date, pidName, eventName, description.getName(), e.getMessage(),
+ description.getEventValueType(), description.getValueType());
+ }
+ }
+
+ // scroll if needed, by showing the last item
+ if (scroll) {
+ int itemCount = mLogTable.getItemCount();
+ if (itemCount > 0) {
+ mLogTable.showItem(mLogTable.getItem(itemCount - 1));
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
+ * the list of descriptors. If an event is configured to be displayed by value and occurrence,
+ * only the values are displayed (as they mark an event occurrence anyway).
+ * <p/>This method is only called when at least one of the descriptor list is non empty.
+ *
+ * @param event
+ * @param logParser
+ * @param valueDescriptors
+ * @param occurrenceDescriptors
+ */
+ protected void addToLog(EventContainer event, EventLogParser logParser,
+ ArrayList<ValueDisplayDescriptor> valueDescriptors,
+ ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
+ ScrollBar bar = mLogTable.getVerticalBar();
+ boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
+
+ // get the date.
+ Calendar c = Calendar.getInstance();
+ long msec = (long) event.sec * 1000L;
+ c.setTimeInMillis(msec);
+
+ // convert the time into a string
+ String date = String.format("%1$tF %1$tT", c);
+
+ String eventName = logParser.getTagMap().get(event.mTag);
+ String pidName = Integer.toString(event.pid);
+
+ if (valueDescriptors.size() > 0) {
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ logDescriptor(event, descriptor, date, pidName, eventName, logParser);
+ }
+ } else {
+ // we display the event. Since the StringBuilder contains the header (date, event name,
+ // pid) at this point, there isn't anything else to display.
+ }
+
+ // scroll if needed, by showing the last item
+ if (scroll) {
+ int itemCount = mLogTable.getItemCount();
+ if (itemCount > 0) {
+ mLogTable.showItem(mLogTable.getItem(itemCount - 1));
+ }
+ }
+ }
+
+
+ /**
+ * Logs a value in the ui.
+ *
+ * @param date
+ * @param pid
+ * @param event
+ * @param valueName
+ * @param value
+ * @param eventValueType
+ * @param valueType
+ */
+ private void logValue(String date, String pid, String event, String valueName,
+ String value, EventContainer.EventValueType eventValueType, EventValueDescription.ValueType valueType) {
+
+ TableItem item = new TableItem(mLogTable, SWT.NONE);
+ item.setText(0, date);
+ item.setText(1, pid);
+ item.setText(2, event);
+ item.setText(3, valueName);
+ item.setText(4, value);
+
+ String type;
+ if (valueType != EventValueDescription.ValueType.NOT_APPLICABLE) {
+ type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
+ } else {
+ type = eventValueType.toString();
+ }
+
+ item.setText(5, type);
+ }
+
+ /**
+ * Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
+ *
+ * @param event the EventContainer
+ * @param descriptor the ValueDisplayDescriptor defining which value to display.
+ * @param date the date of the event in a string.
+ * @param pidName
+ * @param eventName
+ * @param logParser
+ */
+ private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
+ String date, String pidName, String eventName, EventLogParser logParser) {
+
+ String value;
+ try {
+ value = event.getValueAsString(descriptor.valueIndex);
+ } catch (InvalidTypeException e) {
+ value = e.getMessage();
+ }
+
+ EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
+
+ EventValueDescription valueDescription = values[descriptor.valueIndex];
+
+ logValue(date, pidName, eventName, descriptor.valueName, value,
+ valueDescription.getEventValueType(), valueDescription.getValueType());
+ }
+
+ /**
+ * Creates the UI for a log display.
+ *
+ * @param parent the parent {@link Composite}
+ * @param listener the {@link ILogColumnListener} to notify on column resize events.
+ * @return the top Composite of the UI.
+ */
+ private Control createLogUI(Composite parent, final ILogColumnListener listener) {
+ Composite mainComp = new Composite(parent, SWT.NONE);
+ GridLayout gl;
+ mainComp.setLayout(gl = new GridLayout(1, false));
+ gl.marginHeight = gl.marginWidth = 0;
+ mainComp.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ mLogTable = null;
+ }
+ });
+
+ Label l = new Label(mainComp, SWT.CENTER);
+ l.setText(mName);
+ l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
+ SWT.BORDER);
+ mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ IPreferenceStore store = DdmUiPreferences.getStore();
+
+ TableColumn col = TableHelper.createTableColumn(
+ mLogTable, "Time",
+ SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(0, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "pid",
+ SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(1, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Event",
+ SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(2, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Name",
+ SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(3, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Value",
+ SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(4, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Type",
+ SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(5, (TableColumn) source);
+ }
+ }
+ });
+
+ mLogTable.setHeaderVisible(true);
+ mLogTable.setLinesVisible(true);
+
+ return mainComp;
+ }
+
+ /**
+ * Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
+ * <p/>
+ * This does nothing if the <code>Table</code> object is <code>null</code> (because the display
+ * type does not use a column) or if the <code>index</code>-th column is in fact the originating
+ * column passed as argument.
+ *
+ * @param index the index of the column to resize
+ * @param sourceColumn the original column that was resize, and on which we need to sync the
+ * index-th column width.
+ */
+ @Override
+ void resizeColumn(int index, TableColumn sourceColumn) {
+ if (mLogTable != null) {
+ TableColumn col = mLogTable.getColumn(index);
+ if (col != sourceColumn) {
+ col.setWidth(sourceColumn.getWidth());
+ }
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_LOG_ALL;
+ }
+}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java
new file mode 100644
index 0000000..0057c86
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2008 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.ddmuilib.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.labels.CustomXYToolTipGenerator;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.time.FixedMillisecond;
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.util.ShapeUtilities;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+public class DisplaySync extends EventDisplay {
+
+ // Information to graph for each authority
+ private TimePeriodValues mDatasetsSync[];
+ private List<String> mTooltipsSync[];
+ private CustomXYToolTipGenerator mTooltipGenerators[];
+ private TimeSeries mDatasetsSyncTickle[];
+
+ // Dataset of error events to graph
+ private TimeSeries mDatasetError;
+
+ // State information while processing the event stream
+ private int mLastState; // 0 if event started, 1 if event stopped
+ private long mLastStartTime; // ms
+ private long mLastStopTime; //ms
+ private String mLastDetails;
+ private int mLastEvent; // server, poll, etc
+
+ public DisplaySync(String name) {
+ super(name);
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ initSyncDisplay();
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ Control composite = createCompositeChart(parent, logParser, "Sync Status");
+ initSyncDisplay();
+ return composite;
+ }
+
+ /**
+ * Initialize the Plot and series data for the sync display.
+ */
+ void initSyncDisplay() {
+ XYPlot xyPlot = mChart.getXYPlot();
+
+ XYBarRenderer br = new XYBarRenderer();
+ mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
+ mTooltipsSync = new List[NUM_AUTHS];
+ mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
+ mLastDetails = "";
+
+ TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
+ xyPlot.setDataset(tpvc);
+ xyPlot.setRenderer(0, br);
+
+ XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
+ ls.setBaseLinesVisible(false);
+ mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
+ TimeSeriesCollection tsc = new TimeSeriesCollection();
+ xyPlot.setDataset(1, tsc);
+ xyPlot.setRenderer(1, ls);
+
+ mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
+ xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
+ XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
+ errls.setBaseLinesVisible(false);
+ errls.setSeriesPaint(0, Color.RED);
+ xyPlot.setRenderer(2, errls);
+
+ for (int i = 0; i < NUM_AUTHS; i++) {
+ br.setSeriesPaint(i, AUTH_COLORS[i]);
+ ls.setSeriesPaint(i, AUTH_COLORS[i]);
+ mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
+ tpvc.addSeries(mDatasetsSync[i]);
+ mTooltipsSync[i] = new ArrayList<String>();
+ mTooltipGenerators[i] = new CustomXYToolTipGenerator();
+ br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
+ mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
+
+ mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle", FixedMillisecond.class);
+ tsc.addSeries(mDatasetsSyncTickle[i]);
+ ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
+ }
+ }
+
+ /**
+ * Updates the display with a new event. This is the main entry point for
+ * each event. This method has the logic to tie together the start event,
+ * stop event, and details event into one graph item. Note that the details
+ * can happen before or after the stop event.
+ * @param event The event
+ * @param logParser the log parser (unused)
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ try {
+ if (event.mTag == EVENT_SYNC) {
+ int state = Integer.parseInt(event.getValueAsString(1));
+ if (state == 0) { // start
+ mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mLastState = 0;
+ mLastEvent = Integer.parseInt(event.getValueAsString(2));
+ mLastDetails = "";
+ } else if (state == 1) { // stop
+ if (mLastState == 0) {
+ mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ if (mLastStartTime == 0) {
+ // Log starts with a stop event
+ mLastStartTime = mLastStopTime;
+ }
+ addEvent(event);
+ mLastState = 1;
+ }
+ }
+ } else if (event.mTag == EVENT_TICKLE) {
+ int auth = getAuth(event.getValueAsString(0));
+ if (auth >= 0) {
+ long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
+ }
+ } else if (event.mTag == EVENT_SYNC_DETAILS) {
+ int auth = getAuth(event.getValueAsString(0));
+ mLastDetails = event.getValueAsString(3);
+ if (mLastState != 0) { // Not inside event
+ long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ if (updateTime - mLastStopTime <= 250) {
+ // Got details within 250ms after event, so delete and re-insert
+ // Details later than 250ms (arbitrary) are discarded as probably
+ // unrelated.
+ int lastItem = mDatasetsSync[auth].getItemCount();
+ mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
+ mTooltipsSync[auth].remove(lastItem-1);
+ addEvent(event);
+ }
+ }
+ }
+ } catch (InvalidTypeException e) {
+ }
+ }
+
+ /**
+ * Generate the height for an event.
+ * Height is somewhat arbitrarily the count of "things" that happened
+ * during the sync.
+ * When network traffic measurements are available, code should be modified
+ * to use that instead.
+ * @param details The details string associated with the event
+ * @return The height in arbirary units (0-100)
+ */
+ private int getHeightFromDetails(String details) {
+ if (details == null) {
+ return 1; // Arbitrary
+ }
+ int total = 0;
+ String parts[] = details.split("[a-zA-Z]");
+ for (String part : parts) {
+ if ("".equals(part)) continue;
+ total += Integer.parseInt(part);
+ }
+ if (total == 0) {
+ total = 1;
+ }
+ return total;
+ }
+
+ /**
+ * Generates the tooltips text for an event.
+ * This method decodes the cryptic details string.
+ * @param auth The authority associated with the event
+ * @param details The details string
+ * @param eventSource server, poll, etc.
+ * @return The text to display in the tooltips
+ */
+ private String getTextFromDetails(int auth, String details, int eventSource) {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(AUTH_NAMES[auth]).append(": \n");
+
+ Scanner scanner = new Scanner(details);
+ Pattern charPat = Pattern.compile("[a-zA-Z]");
+ Pattern numPat = Pattern.compile("[0-9]+");
+ while (scanner.hasNext()) {
+ String key = scanner.findInLine(charPat);
+ int val = Integer.parseInt(scanner.findInLine(numPat));
+ if (auth == GMAIL && "M".equals(key)) {
+ sb.append("messages from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "L".equals(key)) {
+ sb.append("labels from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "C".equals(key)) {
+ sb.append("check conversation requests from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "A".equals(key)) {
+ sb.append("attachments from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "U".equals(key)) {
+ sb.append("op updates from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "u".equals(key)) {
+ sb.append("op updates to server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "S".equals(key)) {
+ sb.append("send/receive cycles: ").append(val).append("\n");
+ } else if ("Q".equals(key)) {
+ sb.append("queries to server: ").append(val).append("\n");
+ } else if ("E".equals(key)) {
+ sb.append("entries from server: ").append(val).append("\n");
+ } else if ("u".equals(key)) {
+ sb.append("updates from client: ").append(val).append("\n");
+ } else if ("i".equals(key)) {
+ sb.append("inserts from client: ").append(val).append("\n");
+ } else if ("d".equals(key)) {
+ sb.append("deletes from client: ").append(val).append("\n");
+ } else if ("f".equals(key)) {
+ sb.append("full sync requested\n");
+ } else if ("r".equals(key)) {
+ sb.append("partial sync unavailable\n");
+ } else if ("X".equals(key)) {
+ sb.append("hard error\n");
+ } else if ("e".equals(key)) {
+ sb.append("number of parse exceptions: ").append(val).append("\n");
+ } else if ("c".equals(key)) {
+ sb.append("number of conflicts: ").append(val).append("\n");
+ } else if ("a".equals(key)) {
+ sb.append("number of auth exceptions: ").append(val).append("\n");
+ } else if ("D".equals(key)) {
+ sb.append("too many deletions\n");
+ } else if ("R".equals(key)) {
+ sb.append("too many retries: ").append(val).append("\n");
+ } else if ("b".equals(key)) {
+ sb.append("database error\n");
+ } else if ("x".equals(key)) {
+ sb.append("soft error\n");
+ } else if ("l".equals(key)) {
+ sb.append("sync already in progress\n");
+ } else if ("I".equals(key)) {
+ sb.append("io exception\n");
+ } else if (auth == CONTACTS && "p".equals(key)) {
+ sb.append("photos uploaded from client: ").append(val).append("\n");
+ } else if (auth == CONTACTS && "P".equals(key)) {
+ sb.append("photos downloaded from server: ").append(val).append("\n");
+ } else if (auth == CALENDAR && "F".equals(key)) {
+ sb.append("server refresh\n");
+ } else if (auth == CALENDAR && "s".equals(key)) {
+ sb.append("server diffs fetched\n");
+ } else {
+ sb.append(key).append("=").append(val);
+ }
+ }
+ if (eventSource == 0) {
+ sb.append("(server)");
+ } else if (eventSource == 1) {
+ sb.append("(local)");
+ } else if (eventSource == 2) {
+ sb.append("(poll)");
+ } else if (eventSource == 3) {
+ sb.append("(user)");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Helper to add an event to the data series.
+ * Also updates error series if appropriate (x or X in details).
+ * @param event The event
+ */
+ private void addEvent(EventContainer event) {
+ try {
+ int auth = getAuth(event.getValueAsString(0));
+ double height = getHeightFromDetails(mLastDetails);
+ height = height / (mLastStopTime - mLastStartTime + 1) * 10000;
+ if (height > 30) {
+ height = 30;
+ }
+ mDatasetsSync[auth].add(new SimpleTimePeriod(mLastStartTime, mLastStopTime), height);
+ mTooltipsSync[auth].add(getTextFromDetails(auth, mLastDetails,
+ mLastEvent));
+ mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
+ if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
+ long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
+ }
+ } catch (InvalidTypeException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_SYNC;
+ }
+} \ No newline at end of file
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java
new file mode 100644
index 0000000..3087997
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2008 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.ddmuilib.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+public class DisplaySyncHistogram extends EventDisplay {
+
+
+ // State information while processing the event stream
+ protected int mLastState; // 0 if event started, 1 if event stopped
+ protected long mLastStartTime; // ms
+ protected long mLastStopTime; //ms
+ protected String mLastDetails;
+ protected int mLastEvent; // server, poll, etc
+
+ public DisplaySyncHistogram(String name) {
+ super(name);
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ initSyncHistogramDisplay();
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ Control composite = createCompositeChart(parent, logParser, "Sync Histogram");
+ initSyncHistogramDisplay();
+ return composite;
+ }
+
+ // Information to graph for each authority
+ private TimePeriodValues mDatasetsSyncHist[];
+
+ /**
+ * Initializes the display.
+ */
+ private void initSyncHistogramDisplay() {
+ XYPlot xyPlot = mChart.getXYPlot();
+
+ AbstractXYItemRenderer br = new XYBarRenderer();
+ mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
+ mLastDetails = "";
+ mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
+
+ TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
+ xyPlot.setDataset(tpvc);
+ xyPlot.setRenderer(br);
+
+ for (int i = 0; i < NUM_AUTHS + 1; i++) {
+ br.setSeriesPaint(i, AUTH_COLORS[i]);
+ mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
+ tpvc.addSeries(mDatasetsSyncHist[i]);
+ mTimePeriodMap[i] = new HashMap<SimpleTimePeriod, Integer>();
+
+ }
+ }
+
+ /**
+ * Updates the display with a new event. This is the main entry point for
+ * each event. This method has the logic to tie together the start event,
+ * stop event, and details event into one graph item. Note that the details
+ * can happen before or after the stop event.
+ * @param event The event
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ try {
+ if (event.mTag == EVENT_SYNC) {
+ int state = Integer.parseInt(event.getValueAsString(1));
+ if (state == 0) { // start
+ mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mLastState = 0;
+ mLastEvent = Integer.parseInt(event.getValueAsString(2));
+ mLastDetails = "";
+ } else if (state == 1) { // stop
+ if (mLastState == 0) {
+ mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ if (mLastStartTime == 0) {
+ // Log starts with a stop event
+ mLastStartTime = mLastStopTime;
+ }
+ int auth = getAuth(event.getValueAsString(0));
+ if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
+ auth = ERRORS;
+ }
+ double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
+ addHistEvent(event, auth, delta);
+ mLastState = 1;
+ }
+ }
+ } else if (event.mTag == EVENT_SYNC_DETAILS) {
+ int auth = getAuth(event.getValueAsString(0));
+ mLastDetails = event.getValueAsString(3);
+ if (mLastState != 0) { // Not inside event
+ long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ if (updateTime - mLastStopTime <= 250) {
+ // Got details within 250ms after event, so delete and re-insert
+ // Details later than 250ms (arbitrary) are discarded as probably
+ // unrelated.
+ //int lastItem = mDatasetsSync[auth].getItemCount();
+ //addHistEvent(event);
+ if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
+ // Item turns out to be in error, so transfer time from old auth to error.
+
+ double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
+ addHistEvent(event, auth, -delta);
+ addHistEvent(event, ERRORS, delta);
+ }
+ }
+ }
+ }
+ } catch (InvalidTypeException e) {
+ }
+ }
+
+ /**
+ * Helper to add an event to the data series.
+ * Also updates error series if appropriate (x or X in details).
+ * @param event The event
+ * @param auth
+ * @param value
+ */
+ private void addHistEvent(EventContainer event, int auth, double value) {
+ SimpleTimePeriod hour = getTimePeriod(mLastStopTime, mHistWidth);
+
+ // Loop over all datasets to do the stacking.
+ for (int i = auth; i <= ERRORS; i++) {
+ addToPeriod(mDatasetsSyncHist, i, hour, value);
+ }
+ }
+
+ Map<SimpleTimePeriod, Integer> mTimePeriodMap[];
+
+ private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period, double value) {
+ int index;
+ if (mTimePeriodMap[auth].containsKey(period)) {
+ index = mTimePeriodMap[auth].get(period);
+ double oldValue = tpv[auth].getValue(index).doubleValue();
+ tpv[auth].update(index, oldValue + value);
+ } else {
+ index = tpv[auth].getItemCount();
+ mTimePeriodMap[auth].put(period, index);
+ tpv[auth].add(period, value);
+ }
+ }
+
+ /**
+ * Creates a multiple-hour time period for the histogram.
+ * @param time Time in milliseconds.
+ * @param numHoursWide: should divide into a day.
+ * @return SimpleTimePeriod covering the number of hours and containing time.
+ */
+ private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
+ Date date = new Date(time);
+ TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
+ Calendar calendar = Calendar.getInstance(zone);
+ calendar.setTime(date);
+ long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) + calendar.get(Calendar.DAY_OF_YEAR) * 24;
+ int year = calendar.get(Calendar.YEAR);
+ hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
+ calendar.clear();
+ calendar.set(year, 0, 1, 0, 0); // Jan 1
+ long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
+ return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_SYNC_HIST;
+ }
+}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
index e36192c..bbd3e1b 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
@@ -21,86 +21,48 @@ import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventContainer.CompareMethod;
import com.android.ddmlib.log.EventContainer.EventValueType;
import com.android.ddmlib.log.EventLogParser;
-import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.EventValueDescription.ValueType;
import com.android.ddmlib.log.InvalidTypeException;
-import com.android.ddmuilib.DdmUiPreferences;
-import com.android.ddmuilib.TableHelper;
-
-import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
-import org.jfree.chart.axis.AxisLocation;
-import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeEventType;
import org.jfree.chart.event.ChartChangeListener;
-import org.jfree.chart.labels.CustomXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
-import org.jfree.chart.renderer.xy.XYAreaRenderer;
-import org.jfree.chart.renderer.xy.XYBarRenderer;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
-import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.Millisecond;
-import org.jfree.data.time.RegularTimePeriod;
-import org.jfree.data.time.SimpleTimePeriod;
-import org.jfree.data.time.TimePeriodValues;
-import org.jfree.data.time.TimePeriodValuesCollection;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.experimental.swt.SWTUtils;
-import org.jfree.util.ShapeUtilities;
import java.awt.Color;
import java.security.InvalidParameterException;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
import java.util.Set;
-import java.util.TimeZone;
import java.util.regex.Pattern;
/**
* Represents a custom display of one or more events.
*/
-final class EventDisplay {
-
+abstract class EventDisplay {
+
private final static String DISPLAY_DATA_STORAGE_SEPARATOR = ":"; //$NON-NLS-1$
private final static String PID_STORAGE_SEPARATOR = ","; //$NON-NLS-1$
private final static String DESCRIPTOR_STORAGE_SEPARATOR = "$"; //$NON-NLS-1$
private final static String DESCRIPTOR_DATA_STORAGE_SEPARATOR = "!"; //$NON-NLS-1$
-
- private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
- private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
- private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
- private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
- private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
- private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
private final static String FILTER_VALUE_NULL = "<null>"; //$NON-NLS-1$
@@ -111,18 +73,87 @@ final class EventDisplay {
public final static int DISPLAY_TYPE_SYNC_HIST = 4;
private final static int EVENT_CHECK_FAILED = 0;
- private final static int EVENT_CHECK_SAME_TAG = 1;
- private final static int EVENT_CHECK_SAME_VALUE = 2;
-
+ protected final static int EVENT_CHECK_SAME_TAG = 1;
+ protected final static int EVENT_CHECK_SAME_VALUE = 2;
+
+ // Some common variables for sync display. These define the sync backends
+ //and how they should be displayed.
+ protected static final int CALENDAR = 0;
+ protected static final int GMAIL = 1;
+ protected static final int FEEDS = 2;
+ protected static final int CONTACTS = 3;
+ protected static final int ERRORS = 4;
+ protected static final int NUM_AUTHS = (CONTACTS + 1);
+ protected static final String AUTH_NAMES[] = {"Calendar", "Gmail", "Feeds", "Contacts", "Errors"};
+ protected static final Color AUTH_COLORS[] = {Color.MAGENTA, Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
+
+ // Values from data/etc/event-log-tags
+ final int EVENT_SYNC = 2720;
+ final int EVENT_TICKLE = 2742;
+ final int EVENT_SYNC_DETAILS = 2743;
+
+ /**
+ * Creates the appropriate EventDisplay subclass.
+ *
+ * @param type the type of display (DISPLAY_TYPE_LOG_ALL, etc)
+ * @param name the name of the display
+ * @return the created object
+ */
+ public static EventDisplay eventDisplayFactory(int type, String name) {
+ switch (type) {
+ case DISPLAY_TYPE_LOG_ALL:
+ return new DisplayLog(name);
+ case DISPLAY_TYPE_FILTERED_LOG:
+ return new DisplayFilteredLog(name);
+ case DISPLAY_TYPE_SYNC:
+ return new DisplaySync(name);
+ case DISPLAY_TYPE_SYNC_HIST:
+ return new DisplaySyncHistogram(name);
+ case DISPLAY_TYPE_GRAPH:
+ return new DisplayGraph(name);
+ default:
+ throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Adds event to the display.
+ * @param event The event
+ * @param logParser The log parser.
+ */
+ abstract void newEvent(EventContainer event, EventLogParser logParser);
+
+ /**
+ * Resets the display.
+ */
+ abstract void resetUI();
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ abstract int getDisplayType();
+
+ /**
+ * Creates the UI for the event display.
+ *
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ abstract Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener);
+
interface ILogColumnListener {
void columnResized(int index, TableColumn sourceColumn);
}
/**
- * Describes an event to be displayed.
+ * Describes an event to be displayed.
*/
static class OccurrenceDisplayDescriptor {
-
+
int eventTag = -1;
int seriesValueIndex = -1;
boolean includePid = false;
@@ -158,17 +189,19 @@ final class EventDisplay {
/**
* Loads the descriptor parameter from a storage string. The storage string must have
* been generated with {@link #getStorageString()}.
+ *
* @param storageString the storage string
*/
final void loadFrom(String storageString) {
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_DATA_STORAGE_SEPARATOR));
loadFrom(values, 0);
}
-
+
/**
* Loads the parameters from an array of strings.
+ *
* @param storageStrings the strings representing each parameter.
- * @param index the starting index in the array of strings.
+ * @param index the starting index in the array of strings.
* @return the new index in the array.
*/
protected int loadFrom(String[] storageStrings, int index) {
@@ -221,7 +254,7 @@ final class EventDisplay {
}
/**
- * Describes an event value to be displayed.
+ * Describes an event value to be displayed.
*/
static final class ValueDisplayDescriptor extends OccurrenceDisplayDescriptor {
String valueName;
@@ -253,7 +286,7 @@ final class EventDisplay {
void replaceWith(OccurrenceDisplayDescriptor descriptor) {
super.replaceWith(descriptor);
if (descriptor instanceof ValueDisplayDescriptor) {
- ValueDisplayDescriptor valueDescriptor = (ValueDisplayDescriptor)descriptor;
+ ValueDisplayDescriptor valueDescriptor = (ValueDisplayDescriptor) descriptor;
valueName = valueDescriptor.valueName;
valueIndex = valueDescriptor.valueIndex;
}
@@ -261,8 +294,9 @@ final class EventDisplay {
/**
* Loads the parameters from an array of strings.
+ *
* @param storageStrings the strings representing each parameter.
- * @param index the starting index in the array of strings.
+ * @param index the starting index in the array of strings.
* @return the new index in the array.
*/
@Override
@@ -294,100 +328,78 @@ final class EventDisplay {
/* ==================
* Event Display parameters.
* ================== */
- private String mName;
-
- private int mDisplayType = DISPLAY_TYPE_GRAPH;
+ protected String mName;
+
private boolean mPidFiltering = false;
private ArrayList<Integer> mPidFilterList = null;
-
- private final ArrayList<ValueDisplayDescriptor> mValueDescriptors =
- new ArrayList<ValueDisplayDescriptor>();
+
+ protected final ArrayList<ValueDisplayDescriptor> mValueDescriptors =
+ new ArrayList<ValueDisplayDescriptor>();
private final ArrayList<OccurrenceDisplayDescriptor> mOccurrenceDescriptors =
- new ArrayList<OccurrenceDisplayDescriptor>();
+ new ArrayList<OccurrenceDisplayDescriptor>();
/* ==================
* Event Display members for display purpose.
* ================== */
// chart objects
- /** This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series) */
- private final HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>> mValueDescriptorSeriesMap =
- new HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>>();
- /** This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series) */
- private final HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>> mOcurrenceDescriptorSeriesMap =
- new HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>>();
-
- /** This is a map of (ValueType, dataset) */
- private final HashMap<ValueType, TimeSeriesCollection> mValueTypeDataSetMap =
- new HashMap<ValueType, TimeSeriesCollection>();
-
- private JFreeChart mChart;
- private TimeSeriesCollection mOccurrenceDataSet;
- private int mDataSetCount;
+ /**
+ * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
+ */
+ protected final HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>> mValueDescriptorSeriesMap =
+ new HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>>();
+ /**
+ * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
+ */
+ protected final HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>> mOcurrenceDescriptorSeriesMap =
+ new HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>>();
+
+ /**
+ * This is a map of (ValueType, dataset)
+ */
+ protected final HashMap<ValueType, TimeSeriesCollection> mValueTypeDataSetMap =
+ new HashMap<ValueType, TimeSeriesCollection>();
+
+ protected JFreeChart mChart;
+ protected TimeSeriesCollection mOccurrenceDataSet;
+ protected int mDataSetCount;
private ChartComposite mChartComposite;
- private long mMaximumChartItemAge = -1;
- private long mHistWidth = 1;
+ protected long mMaximumChartItemAge = -1;
+ protected long mHistWidth = 1;
// log objects.
- private Table mLogTable;
+ protected Table mLogTable;
/* ==================
* Misc data.
* ================== */
- private int mValueDescriptorCheck = EVENT_CHECK_FAILED;
-
- /**
- * Loads a new {@link EventDisplay} from a storage string. The string must have been created
- * with {@link #getStorageString()}.
- * @param storageString the storage string
- * @return a new {@link EventDisplay} or null if the load failed.
- */
- static EventDisplay load(String storageString) {
- EventDisplay ed = new EventDisplay();
- if (ed.loadFrom(storageString)) {
- return ed;
- }
-
- return null;
- }
+ protected int mValueDescriptorCheck = EVENT_CHECK_FAILED;
EventDisplay(String name) {
mName = name;
}
- /**
- * Builds an {@link EventDisplay}.
- * @param name the name of the display
- * @param displayType the display type: {@link #DISPLAY_TYPE_GRAPH} or
- * {@value #DISPLAY_TYPE_FILTERED_LOG}.
- * @param filterByPid the flag indicating whether to filter by pid.
- */
- EventDisplay(String name, int displayType, boolean filterByPid) {
- mName = name;
- mDisplayType = displayType;
- mPidFiltering = filterByPid;
- }
-
- EventDisplay(EventDisplay from) {
- mName = from.mName;
- mDisplayType = from.mDisplayType;
- mPidFiltering = from.mPidFiltering;
- mMaximumChartItemAge = from.mMaximumChartItemAge;
- mHistWidth = from.mHistWidth;
+ static EventDisplay clone(EventDisplay from) {
+ EventDisplay ed = eventDisplayFactory(from.getDisplayType(), from.getName());
+ ed.mName = from.mName;
+ ed.mPidFiltering = from.mPidFiltering;
+ ed.mMaximumChartItemAge = from.mMaximumChartItemAge;
+ ed.mHistWidth = from.mHistWidth;
if (from.mPidFilterList != null) {
- mPidFilterList = new ArrayList<Integer>();
- mPidFilterList.addAll(from.mPidFilterList);
+ ed.mPidFilterList = new ArrayList<Integer>();
+ ed.mPidFilterList.addAll(from.mPidFilterList);
}
for (ValueDisplayDescriptor desc : from.mValueDescriptors) {
- mValueDescriptors.add(new ValueDisplayDescriptor(desc));
+ ed.mValueDescriptors.add(new ValueDisplayDescriptor(desc));
}
- mValueDescriptorCheck = from.mValueDescriptorCheck;
+ ed.mValueDescriptorCheck = from.mValueDescriptorCheck;
for (OccurrenceDisplayDescriptor desc : from.mOccurrenceDescriptors) {
- mOccurrenceDescriptors.add(new OccurrenceDisplayDescriptor(desc));
+ ed.mOccurrenceDescriptors.add(new OccurrenceDisplayDescriptor(desc));
}
+ return ed;
}
/**
@@ -398,7 +410,7 @@ final class EventDisplay {
sb.append(mName);
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
- sb.append(mDisplayType);
+ sb.append(getDisplayType());
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
sb.append(Boolean.toString(mPidFiltering));
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
@@ -419,35 +431,27 @@ final class EventDisplay {
void setName(String name) {
mName = name;
}
-
+
String getName() {
return mName;
}
-
- void setDisplayType(int value) {
- mDisplayType = value;
- }
-
- int getDisplayType() {
- return mDisplayType;
- }
-
+
void setPidFiltering(boolean filterByPid) {
mPidFiltering = filterByPid;
}
-
+
boolean getPidFiltering() {
return mPidFiltering;
}
-
+
void setPidFilterList(ArrayList<Integer> pids) {
if (mPidFiltering == false) {
new InvalidParameterException();
}
-
+
mPidFilterList = pids;
}
-
+
ArrayList<Integer> getPidFilterList() {
return mPidFilterList;
}
@@ -456,11 +460,11 @@ final class EventDisplay {
if (mPidFiltering == false) {
new InvalidParameterException();
}
-
+
if (mPidFilterList == null) {
mPidFilterList = new ArrayList<Integer>();
}
-
+
mPidFilterList.add(pid);
}
@@ -485,27 +489,29 @@ final class EventDisplay {
Iterator<OccurrenceDisplayDescriptor> getOccurrenceDescriptors() {
return mOccurrenceDescriptors.iterator();
}
-
+
/**
- * Adds a descriptor. This can be a {@link OccurrenceDisplayDescriptor} or a
+ * Adds a descriptor. This can be a {@link OccurrenceDisplayDescriptor} or a
* {@link ValueDisplayDescriptor}.
+ *
* @param descriptor the descriptor to be added.
*/
void addDescriptor(OccurrenceDisplayDescriptor descriptor) {
if (descriptor instanceof ValueDisplayDescriptor) {
- mValueDescriptors.add((ValueDisplayDescriptor)descriptor);
+ mValueDescriptors.add((ValueDisplayDescriptor) descriptor);
mValueDescriptorCheck = checkDescriptors();
} else {
mOccurrenceDescriptors.add(descriptor);
}
}
-
+
/**
* Returns a descriptor by index and class (extending {@link OccurrenceDisplayDescriptor}).
+ *
* @param descriptorClass the class of the descriptor to return.
- * @param index the index of the descriptor to return.
+ * @param index the index of the descriptor to return.
* @return either a {@link OccurrenceDisplayDescriptor} or a {@link ValueDisplayDescriptor}
- * or <code>null</code> if <code>descriptorClass</code> is another class.
+ * or <code>null</code> if <code>descriptorClass</code> is another class.
*/
OccurrenceDisplayDescriptor getDescriptor(
Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
@@ -515,14 +521,15 @@ final class EventDisplay {
} else if (descriptorClass == ValueDisplayDescriptor.class) {
return mValueDescriptors.get(index);
}
-
+
return null;
}
-
+
/**
* Removes a descriptor based on its class and index.
+ *
* @param descriptorClass the class of the descriptor.
- * @param index the index of the descriptor to be removed.
+ * @param index the index of the descriptor to be removed.
*/
void removeDescriptor(Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
if (descriptorClass == OccurrenceDisplayDescriptor.class) {
@@ -533,137 +540,107 @@ final class EventDisplay {
}
}
- /**
- * Creates the UI for the event display.
- * @param parent the parent composite.
- * @param logParser the current log parser.
- * @return the created control (which may have children).
- */
- Control createComposite(final Composite parent, EventLogParser logParser,
- final ILogColumnListener listener) {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- // intended fall-through
- case DISPLAY_TYPE_FILTERED_LOG:
- return createLogUI(parent, listener);
- case DISPLAY_TYPE_GRAPH:
- // intended fall-through
- case DISPLAY_TYPE_SYNC:
- // intended fall-through
- case DISPLAY_TYPE_SYNC_HIST:
- String title = getChartTitle(logParser);
- mChart = ChartFactory.createTimeSeriesChart(
- null,
- null /* timeAxisLabel */,
- null /* valueAxisLabel */,
- null, /* dataset. set below */
- true /* legend */,
- false /* tooltips */,
- false /* urls */);
-
- // get the font to make a proper title. We need to convert the swt font,
- // into an awt font.
- Font f = parent.getFont();
- FontData[] fData = f.getFontData();
-
- // event though on Mac OS there could be more than one fontData, we'll only use
- // the first one.
- FontData firstFontData = fData[0];
-
- java.awt.Font awtFont = SWTUtils.toAwtFont(parent.getDisplay(),
- firstFontData, true /* ensureSameSize */);
-
- if (mDisplayType == DISPLAY_TYPE_SYNC) {
- title = "Sync Status";
- } else if (mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- title = "Sync Histogram";
+ Control createCompositeChart(final Composite parent, EventLogParser logParser,
+ String title) {
+ mChart = ChartFactory.createTimeSeriesChart(
+ null,
+ null /* timeAxisLabel */,
+ null /* valueAxisLabel */,
+ null, /* dataset. set below */
+ true /* legend */,
+ false /* tooltips */,
+ false /* urls */);
+
+ // get the font to make a proper title. We need to convert the swt font,
+ // into an awt font.
+ Font f = parent.getFont();
+ FontData[] fData = f.getFontData();
+
+ // event though on Mac OS there could be more than one fontData, we'll only use
+ // the first one.
+ FontData firstFontData = fData[0];
+
+ java.awt.Font awtFont = SWTUtils.toAwtFont(parent.getDisplay(),
+ firstFontData, true /* ensureSameSize */);
+
+
+ mChart.setTitle(new TextTitle(title, awtFont));
+
+ final XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setRangeCrosshairVisible(true);
+ xyPlot.setRangeCrosshairLockedOnData(true);
+ xyPlot.setDomainCrosshairVisible(true);
+ xyPlot.setDomainCrosshairLockedOnData(true);
+
+ mChart.addChangeListener(new ChartChangeListener() {
+ public void chartChanged(ChartChangeEvent event) {
+ ChartChangeEventType type = event.getType();
+ if (type == ChartChangeEventType.GENERAL) {
+ // because the value we need (rangeCrosshair and domainCrosshair) are
+ // updated on the draw, but the notification happens before the draw,
+ // we process the click in a future runnable!
+ parent.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ processClick(xyPlot);
+ }
+ });
}
+ }
+ });
- mChart.setTitle(new TextTitle(title, awtFont));
-
- final XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setRangeCrosshairVisible(true);
- xyPlot.setRangeCrosshairLockedOnData(true);
- xyPlot.setDomainCrosshairVisible(true);
- xyPlot.setDomainCrosshairLockedOnData(true);
-
- mChart.addChangeListener(new ChartChangeListener() {
- public void chartChanged(ChartChangeEvent event) {
- ChartChangeEventType type = event.getType();
- if (type == ChartChangeEventType.GENERAL) {
- // because the value we need (rangeCrosshair and domainCrosshair) are
- // updated on the draw, but the notification happens before the draw,
- // we process the click in a future runnable!
- parent.getDisplay().asyncExec(new Runnable() {
- public void run() {
- processClick(xyPlot);
- }
- });
- }
- }
- });
-
- mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
- ChartComposite.DEFAULT_WIDTH,
- ChartComposite.DEFAULT_HEIGHT,
- ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
- ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
- 3000, // max draw width. We don't want it to zoom, so we put a big number
- 3000, // max draw height. We don't want it to zoom, so we put a big number
- true, // off-screen buffer
- true, // properties
- true, // save
- true, // print
- true, // zoom
- true); // tooltips
-
- mChartComposite.addDisposeListener(new DisposeListener() {
- public void widgetDisposed(DisposeEvent e) {
- mValueTypeDataSetMap.clear();
- mDataSetCount = 0;
- mOccurrenceDataSet = null;
- mChart = null;
- mChartComposite = null;
- mValueDescriptorSeriesMap.clear();
- mOcurrenceDescriptorSeriesMap.clear();
- }
- });
+ mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
+ ChartComposite.DEFAULT_WIDTH,
+ ChartComposite.DEFAULT_HEIGHT,
+ ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
+ ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
+ 3000, // max draw width. We don't want it to zoom, so we put a big number
+ 3000, // max draw height. We don't want it to zoom, so we put a big number
+ true, // off-screen buffer
+ true, // properties
+ true, // save
+ true, // print
+ true, // zoom
+ true); // tooltips
+
+ mChartComposite.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ mValueTypeDataSetMap.clear();
+ mDataSetCount = 0;
+ mOccurrenceDataSet = null;
+ mChart = null;
+ mChartComposite = null;
+ mValueDescriptorSeriesMap.clear();
+ mOcurrenceDescriptorSeriesMap.clear();
+ }
+ });
- if (mDisplayType == DISPLAY_TYPE_SYNC) {
- initSyncDisplay();
- } else if (mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- initSyncHistDisplay();
- }
+ return mChartComposite;
- return mChartComposite;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
}
private void processClick(XYPlot xyPlot) {
double rangeValue = xyPlot.getRangeCrosshairValue();
- if (rangeValue != 0) {
+ if (rangeValue != 0) {
double domainValue = xyPlot.getDomainCrosshairValue();
-
- Millisecond msec = new Millisecond(new Date((long)domainValue));
-
+
+ Millisecond msec = new Millisecond(new Date((long) domainValue));
+
// look for values in the dataset that contains data at this TimePeriod
Set<ValueDisplayDescriptor> descKeys = mValueDescriptorSeriesMap.keySet();
-
+
for (ValueDisplayDescriptor descKey : descKeys) {
HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descKey);
-
+
Set<Integer> pidKeys = map.keySet();
-
+
for (Integer pidKey : pidKeys) {
TimeSeries series = map.get(pidKey);
-
+
Number value = series.getValue(msec);
if (value != null) {
// found a match. lets check against the actual value.
if (value.doubleValue() == rangeValue) {
-
+
return;
}
}
@@ -672,176 +649,29 @@ final class EventDisplay {
}
}
- /**
- * Creates the UI for a log display.
- * @param parent the parent {@link Composite}
- * @param listener the {@link ILogColumnListener} to notify on column resize events.
- * @return the top Composite of the UI.
- */
- private Control createLogUI(Composite parent, final ILogColumnListener listener) {
- Composite mainComp = new Composite(parent, SWT.NONE);
- GridLayout gl;
- mainComp.setLayout(gl = new GridLayout(1, false));
- gl.marginHeight = gl.marginWidth = 0;
- mainComp.addDisposeListener(new DisposeListener() {
- public void widgetDisposed(DisposeEvent e) {
- mLogTable = null;
- }
- });
-
- Label l = new Label(mainComp, SWT.CENTER);
- l.setText(mName);
- l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
- SWT.BORDER);
- mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- IPreferenceStore store = DdmUiPreferences.getStore();
-
- TableColumn col = TableHelper.createTableColumn(
- mLogTable, "Time",
- SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(0, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "pid",
- SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(1, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Event",
- SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(2, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Name",
- SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(3, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Value",
- SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(4, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Type",
- SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(5, (TableColumn)source);
- }
- }
- });
-
- mLogTable.setHeaderVisible(true);
- mLogTable.setLinesVisible(true);
- return mainComp;
- }
-
/**
* Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
+ * Subclasses can override if necessary.
* <p/>
* This does nothing if the <code>Table</code> object is <code>null</code> (because the display
* type does not use a column) or if the <code>index</code>-th column is in fact the originating
* column passed as argument.
- * @param index the index of the column to resize
+ *
+ * @param index the index of the column to resize
* @param sourceColumn the original column that was resize, and on which we need to sync the
- * index-th column width.
+ * index-th column width.
*/
void resizeColumn(int index, TableColumn sourceColumn) {
- if (mLogTable != null) {
- TableColumn col = mLogTable.getColumn(index);
- if (col != sourceColumn) {
- col.setWidth(sourceColumn.getWidth());
- }
- }
}
-
+
/**
* Sets the current {@link EventLogParser} object.
+ * Subclasses can override if necessary.
*/
- void setNewLogParser(EventLogParser logParser) {
- if (mDisplayType == DISPLAY_TYPE_GRAPH) {
- if (mChart != null) {
- mChart.setTitle(getChartTitle(logParser));
- }
- }
- }
-
- void resetUI() {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- // intended fall-through
- case DISPLAY_TYPE_FILTERED_LOG:
- mLogTable.removeAll();
- break;
- case DISPLAY_TYPE_SYNC:
- initSyncDisplay();
- break;
- case DISPLAY_TYPE_SYNC_HIST:
- initSyncHistDisplay();
- break;
- case DISPLAY_TYPE_GRAPH:
- Collection<TimeSeriesCollection> datasets = mValueTypeDataSetMap.values();
- for (TimeSeriesCollection dataset : datasets) {
- dataset.removeAllSeries();
- }
- if (mOccurrenceDataSet != null) {
- mOccurrenceDataSet.removeAllSeries();
- }
- mValueDescriptorSeriesMap.clear();
- mOcurrenceDescriptorSeriesMap.clear();
- break;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
+ protected void setNewLogParser(EventLogParser logParser) {
}
-
+
/**
* Prepares the {@link EventDisplay} for a multi event display.
*/
@@ -861,102 +691,70 @@ final class EventDisplay {
}
/**
- * Processes a new event. This must be called from the ui thread.
- * @param event the event to process.
- */
- void newEvent(EventContainer event, EventLogParser logParser) {
- ArrayList<ValueDisplayDescriptor> valueDescriptors =
- new ArrayList<ValueDisplayDescriptor>();
-
- ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
- new ArrayList<OccurrenceDisplayDescriptor>();
-
- if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- addToLog(event, logParser);
- break;
- case DISPLAY_TYPE_FILTERED_LOG:
- addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
- break;
- case DISPLAY_TYPE_SYNC:
- updateSyncDisplay(event);
- break;
- case DISPLAY_TYPE_SYNC_HIST:
- updateSyncHistDisplay(event);
- break;
- case DISPLAY_TYPE_GRAPH:
- updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
- break;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
- }
- }
-
- /**
* Returns the {@link Table} object used to display events, if any.
+ *
* @return a Table object or <code>null</code>.
*/
Table getTable() {
return mLogTable;
}
- /** Private constructor used for loading from storage */
- private EventDisplay() {
- // nothing to be done here.
- }
-
/**
- * Loads the {@link EventDisplay} parameters from the storage string.
- * @param storageString the string containing the parameters.
+ * Loads a new {@link EventDisplay} from a storage string. The string must have been created
+ * with {@link #getStorageString()}.
+ *
+ * @param storageString the storage string
+ * @return a new {@link EventDisplay} or null if the load failed.
*/
- private boolean loadFrom(String storageString) {
+ static EventDisplay load(String storageString) {
if (storageString.length() > 0) {
// the storage string is separated by ':'
String[] values = storageString.split(Pattern.quote(DISPLAY_DATA_STORAGE_SEPARATOR));
-
+
try {
int index = 0;
-
- mName = values[index++];
- mDisplayType = Integer.parseInt(values[index++]);
- mPidFiltering = Boolean.parseBoolean(values[index++]);
+
+ String name = values[index++];
+ int displayType = Integer.parseInt(values[index++]);
+ boolean pidFiltering = Boolean.parseBoolean(values[index++]);
+
+ EventDisplay ed = eventDisplayFactory(displayType, name);
+ ed.setPidFiltering(pidFiltering);
// because empty sections are removed by String.split(), we have to check
// the index for those.
if (index < values.length) {
- loadPidFilters(values[index++]);
+ ed.loadPidFilters(values[index++]);
}
-
+
if (index < values.length) {
- loadValueDescriptors(values[index++]);
+ ed.loadValueDescriptors(values[index++]);
}
-
+
if (index < values.length) {
- loadOccurrenceDescriptors(values[index++]);
+ ed.loadOccurrenceDescriptors(values[index++]);
}
-
- updateValueDescriptorCheck();
-
+
+ ed.updateValueDescriptorCheck();
+
if (index < values.length) {
- mMaximumChartItemAge = Long.parseLong(values[index++]);
+ ed.mMaximumChartItemAge = Long.parseLong(values[index++]);
}
if (index < values.length) {
- mHistWidth = Long.parseLong(values[index++]);
+ ed.mHistWidth = Long.parseLong(values[index++]);
}
-
- return true;
+
+ return ed;
} catch (RuntimeException re) {
- // we'll return false below.
+ // we'll return null below.
Log.e("ddms", re);
}
}
-
- return false;
+
+ return null;
}
-
+
private String getPidStorageString() {
if (mPidFilterList != null) {
StringBuilder sb = new StringBuilder();
@@ -969,17 +767,17 @@ final class EventDisplay {
}
sb.append(i);
}
-
+
return sb.toString();
}
return ""; //$NON-NLS-1$
}
-
+
private void loadPidFilters(String storageString) {
if (storageString.length() > 0) {
String[] values = storageString.split(Pattern.quote(PID_STORAGE_SEPARATOR));
-
+
for (String value : values) {
if (mPidFilterList == null) {
mPidFilterList = new ArrayList<Integer>();
@@ -988,12 +786,12 @@ final class EventDisplay {
}
}
}
-
+
private String getDescriptorStorageString(
ArrayList<? extends OccurrenceDisplayDescriptor> descriptorList) {
StringBuilder sb = new StringBuilder();
boolean first = true;
-
+
for (OccurrenceDisplayDescriptor descriptor : descriptorList) {
if (first == false) {
sb.append(DESCRIPTOR_STORAGE_SEPARATOR);
@@ -1002,7 +800,7 @@ final class EventDisplay {
}
sb.append(descriptor.getStorageString());
}
-
+
return sb.toString();
}
@@ -1012,21 +810,21 @@ final class EventDisplay {
}
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
-
+
for (String value : values) {
OccurrenceDisplayDescriptor desc = new OccurrenceDisplayDescriptor();
desc.loadFrom(value);
mOccurrenceDescriptors.add(desc);
}
}
-
+
private void loadValueDescriptors(String storageString) {
if (storageString.length() == 0) {
return;
}
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
-
+
for (String value : values) {
ValueDisplayDescriptor desc = new ValueDisplayDescriptor();
desc.loadFrom(value);
@@ -1035,292 +833,12 @@ final class EventDisplay {
}
/**
- * Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
- * yet created, it is first allocated and set up into the {@link JFreeChart} object.
- */
- private TimeSeriesCollection getOccurrenceDataSet() {
- if (mOccurrenceDataSet == null) {
- mOccurrenceDataSet = new TimeSeriesCollection();
-
- XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
-
- OccurrenceRenderer renderer = new OccurrenceRenderer();
- renderer.setBaseShapesVisible(false);
- xyPlot.setRenderer(mDataSetCount, renderer);
-
- mDataSetCount++;
- }
-
- return mOccurrenceDataSet;
- }
-
- /**
- * Returns a {@link TimeSeriesCollection} for a specific {@link ValueType}.
- * If the data set is not yet created, it is first allocated and set up into the
- * {@link JFreeChart} object.
- * @param type the {@link ValueType} of the data set.
- * @param accumulateValues
- */
- private TimeSeriesCollection getValueDataset(ValueType type, boolean accumulateValues) {
- TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
- if (dataset == null) {
- // create the data set and store it in the map
- dataset = new TimeSeriesCollection();
- mValueTypeDataSetMap.put(type, dataset);
-
- // create the renderer and configure it depending on the ValueType
- AbstractXYItemRenderer renderer;
- if (type == ValueType.PERCENT && accumulateValues) {
- renderer = new XYAreaRenderer();
- } else {
- XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
- r.setBaseShapesVisible(type != ValueType.PERCENT);
-
- renderer = r;
- }
-
- // set both the dataset and the renderer in the plot object.
- XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setDataset(mDataSetCount, dataset);
- xyPlot.setRenderer(mDataSetCount, renderer);
-
- // put a new axis label, and configure it.
- NumberAxis axis = new NumberAxis(type.toString());
-
- if (type == ValueType.PERCENT) {
- // force percent range to be (0,100) fixed.
- axis.setAutoRange(false);
- axis.setRange(0., 100.);
- }
-
- // for the index, we ignore the occurrence dataset
- int count = mDataSetCount;
- if (mOccurrenceDataSet != null) {
- count--;
- }
-
- xyPlot.setRangeAxis(count, axis);
- if ((count % 2) == 0) {
- xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
- } else {
- xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
- }
-
- // now we link the dataset and the axis
- xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
-
- mDataSetCount++;
- }
-
- return dataset;
- }
-
-
- /**
- * Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
- * by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
- * the two lists.
- * <p/>This method is only called when at least one of the descriptor list is non empty.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void updateChart(EventContainer event, EventLogParser logParser,
- ArrayList<ValueDisplayDescriptor> valueDescriptors,
- ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
- Map<Integer, String> tagMap = logParser.getTagMap();
-
- Millisecond millisecondTime = null;
- long msec = -1;
-
- // If the event container is a cpu container (tag == 2721), and there is no descriptor
- // for the total CPU load, then we do accumulate all the values.
- boolean accumulateValues = false;
- double accumulatedValue = 0;
-
- if (event.mTag == 2721) {
- accumulateValues = true;
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- accumulateValues &= (descriptor.valueIndex != 0);
- }
- }
-
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- try {
- // get the hashmap for this descriptor
- HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descriptor);
-
- // if it's not there yet, we create it.
- if (map == null) {
- map = new HashMap<Integer, TimeSeries>();
- mValueDescriptorSeriesMap.put(descriptor, map);
- }
-
- // get the TimeSeries for this pid
- TimeSeries timeSeries = map.get(event.pid);
-
- // if it doesn't exist yet, we create it
- if (timeSeries == null) {
- // get the series name
- String seriesFullName = null;
- String seriesLabel = getSeriesLabel(event, descriptor);
-
- switch (mValueDescriptorCheck) {
- case EVENT_CHECK_SAME_TAG:
- seriesFullName = String.format("%1$s / %2$s", seriesLabel,
- descriptor.valueName);
- break;
- case EVENT_CHECK_SAME_VALUE:
- seriesFullName = String.format("%1$s", seriesLabel);
- break;
- default:
- seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
- tagMap.get(descriptor.eventTag),
- descriptor.valueName);
- break;
- }
-
- // get the data set for this ValueType
- TimeSeriesCollection dataset = getValueDataset(
- logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
- .getValueType(),
- accumulateValues);
-
- // create the series
- timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
- if (mMaximumChartItemAge != -1) {
- timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
- }
-
- dataset.addSeries(timeSeries);
-
- // add it to the map.
- map.put(event.pid, timeSeries);
- }
-
- // update the timeSeries.
-
- // get the value from the event
- double value = event.getValueAsDouble(descriptor.valueIndex);
-
- // accumulate the values if needed.
- if (accumulateValues) {
- accumulatedValue += value;
- value = accumulatedValue;
- }
-
- // get the time
- if (millisecondTime == null) {
- msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- millisecondTime = new Millisecond(new Date(msec));
- }
-
- // add the value to the time series
- timeSeries.addOrUpdate(millisecondTime, value);
- } catch (InvalidTypeException e) {
- // just ignore this descriptor if there's a type mismatch
- }
- }
-
- for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
- try {
- // get the hashmap for this descriptor
- HashMap<Integer, TimeSeries> map = mOcurrenceDescriptorSeriesMap.get(descriptor);
-
- // if it's not there yet, we create it.
- if (map == null) {
- map = new HashMap<Integer, TimeSeries>();
- mOcurrenceDescriptorSeriesMap.put(descriptor, map);
- }
-
- // get the TimeSeries for this pid
- TimeSeries timeSeries = map.get(event.pid);
-
- // if it doesn't exist yet, we create it.
- if (timeSeries == null) {
- String seriesLabel = getSeriesLabel(event, descriptor);
-
- String seriesFullName = String.format("[%1$s:%2$s]",
- tagMap.get(descriptor.eventTag), seriesLabel);
-
- timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
- if (mMaximumChartItemAge != -1) {
- timeSeries.setMaximumItemAge(mMaximumChartItemAge);
- }
-
- getOccurrenceDataSet().addSeries(timeSeries);
-
- map.put(event.pid, timeSeries);
- }
-
- // update the series
-
- // get the time
- if (millisecondTime == null) {
- msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- millisecondTime = new Millisecond(new Date(msec));
- }
-
- // add the value to the time series
- timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
- } catch (InvalidTypeException e) {
- // just ignore this descriptor if there's a type mismatch
- }
- }
-
- // go through all the series and remove old values.
- if (msec != -1 && mMaximumChartItemAge != -1) {
- Collection<HashMap<Integer, TimeSeries>> pidMapValues =
- mValueDescriptorSeriesMap.values();
-
- for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
- Collection<TimeSeries> seriesCollection = pidMapValue.values();
-
- for (TimeSeries timeSeries : seriesCollection) {
- timeSeries.removeAgedItems(msec, true);
- }
- }
-
- pidMapValues = mOcurrenceDescriptorSeriesMap.values();
- for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
- Collection<TimeSeries> seriesCollection = pidMapValue.values();
-
- for (TimeSeries timeSeries : seriesCollection) {
- timeSeries.removeAgedItems(msec, true);
- }
- }
- }
- }
-
- /**
- * Return the series label for this event. This only contains the pid information.
- * @param event the {@link EventContainer}
- * @param descriptor the {@link OccurrenceDisplayDescriptor}
- * @return the series label.
- * @throws InvalidTypeException
- */
- private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
- throws InvalidTypeException {
- if (descriptor.seriesValueIndex != -1) {
- if (descriptor.includePid == false) {
- return event.getValueAsString(descriptor.seriesValueIndex);
- } else {
- return String.format("%1$s (%2$d)",
- event.getValueAsString(descriptor.seriesValueIndex), event.pid);
- }
- }
-
- return Integer.toString(event.pid);
- }
-
- /**
* Fills a list with {@link OccurrenceDisplayDescriptor} (or a subclass of it) from another
* list if they are configured to display the {@link EventContainer}
- * @param event the event container
+ *
+ * @param event the event container
* @param fullList the list with all the descriptors.
- * @param outList the list to fill.
+ * @param outList the list to fill.
*/
@SuppressWarnings("unchecked")
private void getDescriptors(EventContainer event,
@@ -1348,200 +866,21 @@ final class EventDisplay {
}
}
}
-
- /**
- * Adds an {@link EventContainer} to the log.
- * @param event the event.
- * @param logParser the log parser.
- */
- private void addToLog(EventContainer event, EventLogParser logParser) {
- ScrollBar bar = mLogTable.getVerticalBar();
- boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
-
- // get the date.
- Calendar c = Calendar.getInstance();
- long msec = (long)event.sec * 1000L;
- c.setTimeInMillis(msec);
-
- // convert the time into a string
- String date = String.format("%1$tF %1$tT", c);
-
- String eventName = logParser.getTagMap().get(event.mTag);
- String pidName = Integer.toString(event.pid);
-
- // get the value description
- EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
- if (valueDescription != null) {
- for (int i = 0 ; i < valueDescription.length ; i++) {
- EventValueDescription description = valueDescription[i];
- try {
- String value = event.getValueAsString(i);
-
- logValue(date, pidName, eventName, description.getName(), value,
- description.getEventValueType(), description.getValueType());
- } catch (InvalidTypeException e) {
- logValue(date, pidName, eventName, description.getName(), e.getMessage(),
- description.getEventValueType(), description.getValueType());
- }
- }
-
- // scroll if needed, by showing the last item
- if (scroll) {
- int itemCount = mLogTable.getItemCount();
- if (itemCount > 0) {
- mLogTable.showItem(mLogTable.getItem(itemCount-1));
- }
- }
- }
- }
-
- /**
- * Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
- * the list of descriptors. If an event is configured to be displayed by value and occurrence,
- * only the values are displayed (as they mark an event occurrence anyway).
- * <p/>This method is only called when at least one of the descriptor list is non empty.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void addToLog(EventContainer event, EventLogParser logParser,
- ArrayList<ValueDisplayDescriptor> valueDescriptors,
- ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
- ScrollBar bar = mLogTable.getVerticalBar();
- boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
-
- // get the date.
- Calendar c = Calendar.getInstance();
- long msec = (long)event.sec * 1000L;
- c.setTimeInMillis(msec);
-
- // convert the time into a string
- String date = String.format("%1$tF %1$tT", c);
-
- String eventName = logParser.getTagMap().get(event.mTag);
- String pidName = Integer.toString(event.pid);
-
- if (valueDescriptors.size() > 0) {
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- logDescriptor(event, descriptor, date, pidName, eventName, logParser);
- }
- } else {
- // we display the event. Since the StringBuilder contains the header (date, event name,
- // pid) at this point, there isn't anything else to display.
- }
-
- // scroll if needed, by showing the last item
- if (scroll) {
- int itemCount = mLogTable.getItemCount();
- if (itemCount > 0) {
- mLogTable.showItem(mLogTable.getItem(itemCount-1));
- }
- }
- }
-
- /**
- * Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
- * @param event the EventContainer
- * @param descriptor the ValueDisplayDescriptor defining which value to display.
- * @param date the date of the event in a string.
- * @param pidName
- * @param eventName
- * @param logParser
- */
- private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
- String date, String pidName, String eventName, EventLogParser logParser) {
-
- String value;
- try {
- value = event.getValueAsString(descriptor.valueIndex);
- } catch (InvalidTypeException e) {
- value = e.getMessage();
- }
-
- EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
-
- EventValueDescription valueDescription = values[descriptor.valueIndex];
-
- logValue(date, pidName, eventName, descriptor.valueName, value,
- valueDescription.getEventValueType(), valueDescription.getValueType());
- }
-
- /**
- * Logs a value in the ui.
- * @param date
- * @param pid
- * @param event
- * @param valueName
- * @param value
- * @param eventValueType
- * @param valueType
- */
- private void logValue(String date, String pid, String event, String valueName,
- String value, EventValueType eventValueType, ValueType valueType) {
-
- TableItem item = new TableItem(mLogTable, SWT.NONE);
- item.setText(0, date);
- item.setText(1, pid);
- item.setText(2, event);
- item.setText(3, valueName);
- item.setText(4, value);
-
- String type;
- if (valueType != ValueType.NOT_APPLICABLE) {
- type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
- } else {
- type = eventValueType.toString();
- }
-
- item.setText(5, type);
- }
/**
- * Show the current value(s) of an {@link EventContainer}. The values to show are defined by
- * the {@link ValueDisplayDescriptor}s and {@link OccurrenceDisplayDescriptor}s passed in the
- * two lists.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void showCurrent(EventContainer event, EventLogParser logParser,
- ArrayList<ValueDisplayDescriptor> valueDescriptors,
- ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
- // TODO Auto-generated method stub
- }
-
- // Values from data/etc/event-log-tags
- final int EVENT_SYNC = 2720;
- final int EVENT_TICKLE = 2742;
- final int EVENT_SYNC_DETAILS = 2743;
-
- /**
- * Filters the {@link EventContainer}, and fills two list of {@link ValueDisplayDescriptor}
- * and {@link OccurrenceDisplayDescriptor} configured to display the event.
+ * Filters the {@link com.android.ddmlib.log.EventContainer}, and fills two list of {@link com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor}
+ * and {@link com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor} configured to display the event.
+ *
* @param event
* @param valueDescriptors
* @param occurrenceDescriptors
* @return true if the event should be displayed.
*/
- private boolean filterEvent(EventContainer event,
+
+ protected boolean filterEvent(EventContainer event,
ArrayList<ValueDisplayDescriptor> valueDescriptors,
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
- if (mDisplayType == DISPLAY_TYPE_LOG_ALL) {
- return true;
- }
-
- if (mDisplayType == DISPLAY_TYPE_SYNC || mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- if (event.mTag == EVENT_SYNC || event.mTag == EVENT_TICKLE ||
- event.mTag == EVENT_SYNC_DETAILS) {
- return true;
- } else {
- return false;
- }
- }
-
// test the pid first (if needed)
if (mPidFiltering && mPidFilterList != null) {
boolean found = false;
@@ -1551,7 +890,7 @@ final class EventDisplay {
break;
}
}
-
+
if (found == false) {
return false;
}
@@ -1569,7 +908,8 @@ final class EventDisplay {
* Checks all the {@link ValueDisplayDescriptor} for similarity.
* If all the event values are from the same tag, the method will return EVENT_CHECK_SAME_TAG.
* If all the event/value are the same, the method will return EVENT_CHECK_SAME_VALUE
- * @return
+ *
+ * @return flag as described above
*/
private int checkDescriptors() {
if (mValueDescriptors.size() < 2) {
@@ -1598,39 +938,8 @@ final class EventDisplay {
if (index == -1) {
return EVENT_CHECK_SAME_TAG;
}
-
- return EVENT_CHECK_SAME_VALUE;
- }
-
- /**
- * Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
- * @param logParser the logParser.
- * @return the chart title.
- */
- private String getChartTitle(EventLogParser logParser) {
- if (mValueDescriptors.size() > 0) {
- String chartDesc = null;
- switch (mValueDescriptorCheck) {
- case EVENT_CHECK_SAME_TAG:
- if (logParser != null) {
- chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
- }
- break;
- case EVENT_CHECK_SAME_VALUE:
- if (logParser != null) {
- chartDesc = String.format("%1$s / %2$s",
- logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
- mValueDescriptors.get(0).valueName);
- }
- break;
- }
-
- if (chartDesc != null) {
- return String.format("%1$s - %2$s", mName, chartDesc);
- }
- }
- return mName;
+ return EVENT_CHECK_SAME_VALUE;
}
/**
@@ -1642,17 +951,19 @@ final class EventDisplay {
/**
* Sets the time limit on the charts.
+ *
* @param timeLimit the time limit in seconds.
*/
void setChartTimeLimit(long timeLimit) {
mMaximumChartItemAge = timeLimit;
}
-
+
long getChartTimeLimit() {
return mMaximumChartItemAge;
}
/**
+ * m
* Resets the histogram width
*/
void resetHistWidth() {
@@ -1661,150 +972,24 @@ final class EventDisplay {
/**
* Sets the histogram width
+ *
* @param histWidth the width in hours
*/
void setHistWidth(long histWidth) {
mHistWidth = histWidth;
}
-
+
long getHistWidth() {
return mHistWidth;
}
- // Implementation of the Sync display
- // TODO: DISPLAY_TYPE_LOG, DISPLAY_TYPE_GRAPH, and DISPLAY_TYPE_SYNC should be subclasses
- // of EventDisplay.java
-
- private static final int CALENDAR = 0;
- private static final int GMAIL = 1;
- private static final int FEEDS = 2;
- private static final int CONTACTS = 3;
- private static final int ERRORS = 4;
- private static final int NUM_AUTHS = (CONTACTS+1);
- private static final String AUTH_NAMES[] = {"Calendar", "Gmail", "Feeds", "Contacts", "Errors"};
- private static final Color AUTH_COLORS[] = {Color.MAGENTA, Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
-
- // Information to graph for each authority
- private TimePeriodValues mDatasetsSync[];
- private List<String> mTooltipsSync[];
- private CustomXYToolTipGenerator mTooltipGenerators[];
- private TimeSeries mDatasetsSyncTickle[];
-
- // Dataset of error events to graph
- private TimeSeries mDatasetError;
-
- /**
- * Initialize the Plot and series data for the sync display.
- */
- void initSyncDisplay() {
- XYPlot xyPlot = mChart.getXYPlot();
-
- XYBarRenderer br = new XYBarRenderer();
- mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
- mTooltipsSync = new List[NUM_AUTHS];
- mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
- mLastDetails = "";
-
- TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
- xyPlot.setDataset(tpvc);
- xyPlot.setRenderer(0, br);
-
- XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
- ls.setBaseLinesVisible(false);
- mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
- TimeSeriesCollection tsc = new TimeSeriesCollection();
- xyPlot.setDataset(1, tsc);
- xyPlot.setRenderer(1, ls);
-
- mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
- xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
- XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
- errls.setBaseLinesVisible(false);
- errls.setSeriesPaint(0, Color.RED);
- xyPlot.setRenderer(2, errls);
-
- for (int i = 0; i < NUM_AUTHS; i++) {
- br.setSeriesPaint(i, AUTH_COLORS[i]);
- ls.setSeriesPaint(i, AUTH_COLORS[i]);
- mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
- tpvc.addSeries(mDatasetsSync[i]);
- mTooltipsSync[i] = new ArrayList<String>();
- mTooltipGenerators[i] = new CustomXYToolTipGenerator();
- br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
- mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
-
- mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle", FixedMillisecond.class);
- tsc.addSeries(mDatasetsSyncTickle[i]);
- ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
- }
- }
-
- // State information while processing the event stream
- private int mLastState; // 0 if event started, 1 if event stopped
- private long mLastStartTime; // ms
- private long mLastStopTime; //ms
- private String mLastDetails;
- private int mLastEvent; // server, poll, etc
-
- /**
- * Updates the display with a new event. This is the main entry point for
- * each event. This method has the logic to tie together the start event,
- * stop event, and details event into one graph item. Note that the details
- * can happen before or after the stop event.
- * @param event The event
- */
- private void updateSyncDisplay(EventContainer event) {
- try {
- if (event.mTag == EVENT_SYNC) {
- int state = Integer.parseInt(event.getValueAsString(1));
- if (state == 0) { // start
- mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mLastState = 0;
- mLastEvent = Integer.parseInt(event.getValueAsString(2));
- mLastDetails = "";
- } else if (state == 1) { // stop
- if (mLastState == 0) {
- mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (mLastStartTime == 0) {
- // Log starts with a stop event
- mLastStartTime = mLastStopTime;
- }
- addEvent(event);
- mLastState = 1;
- }
- }
- } else if (event.mTag == EVENT_TICKLE) {
- int auth = getAuth(event.getValueAsString(0));
- if (auth >= 0) {
- long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
- }
- } else if (event.mTag == EVENT_SYNC_DETAILS) {
- int auth = getAuth(event.getValueAsString(0));
- mLastDetails = event.getValueAsString(3);
- if (mLastState != 0) { // Not inside event
- long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (updateTime - mLastStopTime <= 250) {
- // Got details within 250ms after event, so delete and re-insert
- // Details later than 250ms (arbitrary) are discarded as probably
- // unrelated.
- int lastItem = mDatasetsSync[auth].getItemCount();
- mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
- mTooltipsSync[auth].remove(lastItem-1);
- addEvent(event);
- }
- }
- }
- } catch (InvalidTypeException e) {
- }
- }
-
/**
* Convert authority name to auth number.
+ *
* @param authname "calendar", etc.
* @return number series number associated with the authority
*/
- private int getAuth(String authname) throws InvalidTypeException {
+ protected int getAuth(String authname) throws InvalidTypeException {
if ("calendar".equals(authname) || "cl".equals(authname)) {
return CALENDAR;
} else if ("contacts".equals(authname) || "cp".equals(authname)) {
@@ -1821,283 +1006,4 @@ final class EventDisplay {
throw new InvalidTypeException("Unknown authname " + authname);
}
}
-
- /**
- * Generate the height for an event.
- * Height is somewhat arbitrarily the count of "things" that happened
- * during the sync.
- * When network traffic measurements are available, code should be modified
- * to use that instead.
- * @param details The details string associated with the event
- * @return The height in arbirary units (0-100)
- */
- private int getHeightFromDetails(String details) {
- if (details == null) {
- return 1; // Arbitrary
- }
- int total = 0;
- String parts[] = details.split("[a-zA-Z]");
- for (String part : parts) {
- if ("".equals(part)) continue;
- total += Integer.parseInt(part);
- }
- if (total == 0) {
- total = 1;
- }
- return total;
- }
-
- /**
- * Generates the tooltips text for an event.
- * This method decodes the cryptic details string.
- * @param auth The authority associated with the event
- * @param details The details string
- * @param eventSource server, poll, etc.
- * @return The text to display in the tooltips
- */
- private String getTextFromDetails(int auth, String details, int eventSource) {
-
- StringBuffer sb = new StringBuffer();
- sb.append(AUTH_NAMES[auth]).append(": \n");
-
- Scanner scanner = new Scanner(details);
- Pattern charPat = Pattern.compile("[a-zA-Z]");
- Pattern numPat = Pattern.compile("[0-9]+");
- while (scanner.hasNext()) {
- String key = scanner.findInLine(charPat);
- int val = Integer.parseInt(scanner.findInLine(numPat));
- if (auth == GMAIL && "M".equals(key)) {
- sb.append("messages from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "L".equals(key)) {
- sb.append("labels from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "C".equals(key)) {
- sb.append("check conversation requests from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "A".equals(key)) {
- sb.append("attachments from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "U".equals(key)) {
- sb.append("op updates from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "u".equals(key)) {
- sb.append("op updates to server: ").append(val).append("\n");
- } else if (auth == GMAIL && "S".equals(key)) {
- sb.append("send/receive cycles: ").append(val).append("\n");
- } else if ("Q".equals(key)) {
- sb.append("queries to server: ").append(val).append("\n");
- } else if ("E".equals(key)) {
- sb.append("entries from server: ").append(val).append("\n");
- } else if ("u".equals(key)) {
- sb.append("updates from client: ").append(val).append("\n");
- } else if ("i".equals(key)) {
- sb.append("inserts from client: ").append(val).append("\n");
- } else if ("d".equals(key)) {
- sb.append("deletes from client: ").append(val).append("\n");
- } else if ("f".equals(key)) {
- sb.append("full sync requested\n");
- } else if ("r".equals(key)) {
- sb.append("partial sync unavailable\n");
- } else if ("X".equals(key)) {
- sb.append("hard error\n");
- } else if ("e".equals(key)) {
- sb.append("number of parse exceptions: ").append(val).append("\n");
- } else if ("c".equals(key)) {
- sb.append("number of conflicts: ").append(val).append("\n");
- } else if ("a".equals(key)) {
- sb.append("number of auth exceptions: ").append(val).append("\n");
- } else if ("D".equals(key)) {
- sb.append("too many deletions\n");
- } else if ("R".equals(key)) {
- sb.append("too many retries: ").append(val).append("\n");
- } else if ("b".equals(key)) {
- sb.append("database error\n");
- } else if ("x".equals(key)) {
- sb.append("soft error\n");
- } else if ("l".equals(key)) {
- sb.append("sync already in progress\n");
- } else if ("I".equals(key)) {
- sb.append("io exception\n");
- } else if (auth == CONTACTS && "p".equals(key)) {
- sb.append("photos uploaded from client: ").append(val).append("\n");
- } else if (auth == CONTACTS && "P".equals(key)) {
- sb.append("photos downloaded from server: ").append(val).append("\n");
- } else if (auth == CALENDAR && "F".equals(key)) {
- sb.append("server refresh\n");
- } else if (auth == CALENDAR && "s".equals(key)) {
- sb.append("server diffs fetched\n");
- } else {
- sb.append(key).append("=").append(val);
- }
- }
- if (eventSource == 0) {
- sb.append("(server)");
- } else if (eventSource == 1) {
- sb.append("(local)");
- } else if (eventSource == 2) {
- sb.append("(poll)");
- } else if (eventSource == 3) {
- sb.append("(user)");
- }
- return sb.toString();
- }
-
- /**
- * Helper to add an event to the data series.
- * Also updates error series if appropriate (x or X in details).
- * @param event The event
- */
- private void addEvent(EventContainer event) {
- try {
- int auth = getAuth(event.getValueAsString(0));
- double height = getHeightFromDetails(mLastDetails);
- height = height / (mLastStopTime - mLastStartTime + 1) * 10000;
- if (height > 30) {
- height = 30;
- }
- mDatasetsSync[auth].add(new SimpleTimePeriod(mLastStartTime, mLastStopTime), height);
- mTooltipsSync[auth].add(getTextFromDetails(auth, mLastDetails,
- mLastEvent));
- mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
- }
- } catch (InvalidTypeException e) {
- e.printStackTrace();
- }
- }
-
- // Implementation of the Sync Histogram display
-
- // Information to graph for each authority
- private TimePeriodValues mDatasetsSyncHist[];
-
- /**
- * Initialize the Plot and series data for the sync display.
- */
- void initSyncHistDisplay() {
- XYPlot xyPlot = mChart.getXYPlot();
-
- AbstractXYItemRenderer br = new XYBarRenderer();
- mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
- mLastDetails = "";
- mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
-
- TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
- xyPlot.setDataset(tpvc);
- xyPlot.setRenderer(br);
-
- for (int i = 0; i < NUM_AUTHS + 1; i++) {
- br.setSeriesPaint(i, AUTH_COLORS[i]);
- mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
- tpvc.addSeries(mDatasetsSyncHist[i]);
- mTimePeriodMap[i] = new HashMap<SimpleTimePeriod, Integer>();
-
- }
- }
-
- /**
- * Updates the display with a new event. This is the main entry point for
- * each event. This method has the logic to tie together the start event,
- * stop event, and details event into one graph item. Note that the details
- * can happen before or after the stop event.
- * @param event The event
- */
- private void updateSyncHistDisplay(EventContainer event) {
- try {
- if (event.mTag == EVENT_SYNC) {
- int state = Integer.parseInt(event.getValueAsString(1));
- if (state == 0) { // start
- mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mLastState = 0;
- mLastEvent = Integer.parseInt(event.getValueAsString(2));
- mLastDetails = "";
- } else if (state == 1) { // stop
- if (mLastState == 0) {
- mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (mLastStartTime == 0) {
- // Log starts with a stop event
- mLastStartTime = mLastStopTime;
- }
- int auth = getAuth(event.getValueAsString(0));
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- auth = ERRORS;
- }
- double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
- addHistEvent(event, auth, delta);
- mLastState = 1;
- }
- }
- } else if (event.mTag == EVENT_SYNC_DETAILS) {
- int auth = getAuth(event.getValueAsString(0));
- mLastDetails = event.getValueAsString(3);
- if (mLastState != 0) { // Not inside event
- long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (updateTime - mLastStopTime <= 250) {
- // Got details within 250ms after event, so delete and re-insert
- // Details later than 250ms (arbitrary) are discarded as probably
- // unrelated.
- //int lastItem = mDatasetsSync[auth].getItemCount();
- //addHistEvent(event);
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- // Item turns out to be in error, so transfer time from old auth to error.
-
- double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
- addHistEvent(event, auth, -delta);
- addHistEvent(event, ERRORS, delta);
- }
- }
- }
- }
- } catch (InvalidTypeException e) {
- }
- }
-
- /**
- * Helper to add an event to the data series.
- * Also updates error series if appropriate (x or X in details).
- * @param event The event
- * @param auth
- * @param value
- */
- private void addHistEvent(EventContainer event, int auth, double value) {
- SimpleTimePeriod hour = getTimePeriod(mLastStopTime, mHistWidth);
-
- // Loop over all datasets to do the stacking.
- for (int i = auth; i <= ERRORS; i++) {
- addToPeriod(mDatasetsSyncHist, i, hour, value);
- }
- }
-
- Map<SimpleTimePeriod, Integer> mTimePeriodMap[];
-
- private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period, double value) {
- int index;
- if (mTimePeriodMap[auth].containsKey(period)) {
- index = mTimePeriodMap[auth].get(period);
- double oldValue = tpv[auth].getValue(index).doubleValue();
- tpv[auth].update(index, oldValue + value);
- } else {
- index = tpv[auth].getItemCount();
- mTimePeriodMap[auth].put(period, index);
- tpv[auth].add(period, value);
- }
- }
-
- /**
- * Creates a multiple-hour time period for the histogram.
- * @param time Time in milliseconds.
- * @param numHoursWide: should divide into a day.
- * @return SimpleTimePeriod covering the number of hours and containing time.
- */
- private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
- Date date = new Date(time);
- TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
- Calendar calendar = Calendar.getInstance(zone);
- calendar.setTime(date);
- long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) + calendar.get(Calendar.DAY_OF_YEAR) * 24;
- int year = calendar.get(Calendar.YEAR);
- hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
- calendar.clear();
- calendar.set(year, 0, 1, 0, 0); // Jan 1
- long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
- return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
- }
}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
index 94f04d7..b9daa41 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
@@ -23,7 +23,6 @@ import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.IImageLoader;
import com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor;
import com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor;
-
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
@@ -444,10 +443,13 @@ class EventDisplayOptions extends Dialog {
@Override
public void widgetSelected(SelectionEvent e) {
EventDisplay eventDisplay = getCurrentEventDisplay();
- if (eventDisplay != null) {
+ if (eventDisplay != null && eventDisplay.getDisplayType() != mDisplayTypeCombo.getSelectionIndex()) {
+ /* Replace the EventDisplay object with a different subclass */
setModified();
- eventDisplay.setDisplayType(mDisplayTypeCombo.getSelectionIndex());
- fillUiWith(eventDisplay);
+ String name = eventDisplay.getName();
+ EventDisplay newEventDisplay = EventDisplay.eventDisplayFactory(mDisplayTypeCombo.getSelectionIndex(), name);
+ setCurrentEventDisplay(newEventDisplay);
+ fillUiWith(newEventDisplay);
}
}
});
@@ -693,7 +695,7 @@ class EventDisplayOptions extends Dialog {
private void duplicateEventDisplay(ArrayList<EventDisplay> displayList) {
for (EventDisplay eventDisplay : displayList) {
- mDisplayList.add(new EventDisplay(eventDisplay));
+ mDisplayList.add(EventDisplay.clone(eventDisplay));
}
}
@@ -744,7 +746,7 @@ class EventDisplayOptions extends Dialog {
String name = String.format("display %1$d", count + 1);
- EventDisplay eventDisplay = new EventDisplay(name);
+ EventDisplay eventDisplay = EventDisplay.eventDisplayFactory(0 /* type*/, name);
mDisplayList.add(eventDisplay);
mEventDisplayList.add(name);
@@ -779,6 +781,13 @@ class EventDisplayOptions extends Dialog {
return null;
}
+
+ private void setCurrentEventDisplay(EventDisplay eventDisplay) {
+ int selection = mEventDisplayList.getSelectionIndex();
+ if (selection != -1) {
+ mDisplayList.set(selection, eventDisplay);
+ }
+ }
private void handleEventDisplaySelection() {
EventDisplay eventDisplay = getCurrentEventDisplay();
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
index 2ace78a..a1303f6 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
@@ -38,7 +38,7 @@ public class EventLogImporter {
if (top == null) {
throw new FileNotFoundException();
}
- final String tagFile = top + "/data/etc/event-log-tags";
+ final String tagFile = top + "/system/core/logcat/event-log-tags";
BufferedReader tagReader = new BufferedReader(
new InputStreamReader(new FileInputStream(tagFile)));
BufferedReader eventReader = new BufferedReader(
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 6743246..82bcea8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -181,7 +181,7 @@ public class ApkBuilder extends BaseBuilder {
return mMakeFinalPackage;
}
}
-
+
/**
* {@link IZipEntryFilter} to filter out everything that is not a standard java resources.
* <p/>Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when
@@ -215,6 +215,7 @@ public class ApkBuilder extends BaseBuilder {
// First thing we do is go through the resource delta to not
// lose it if we have to abort the build for any reason.
+ ApkDeltaVisitor dv = null;
if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Start_Full_Apk_Build);
@@ -233,22 +234,13 @@ public class ApkBuilder extends BaseBuilder {
mConvertToDex = true;
mBuildFinalPackage = true;
} else {
- ApkDeltaVisitor dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
+ dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
delta.accept(dv);
// save the state
mPackageResources |= dv.getPackageResources();
mConvertToDex |= dv.getConvertToDex();
mBuildFinalPackage |= dv.getMakeFinalPackage();
-
- if (dv.mXmlError) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Xml_Error);
-
- // if there was some XML errors, we just return w/o doing
- // anything since we've put some markers in the files anyway
- return referencedProjects;
- }
}
// also go through the delta for all the referenced projects, until we are forced to
@@ -258,13 +250,13 @@ public class ApkBuilder extends BaseBuilder {
IJavaProject referencedJavaProject = referencedJavaProjects[i];
delta = getDelta(referencedJavaProject.getProject());
if (delta != null) {
- ReferencedProjectDeltaVisitor dv = new ReferencedProjectDeltaVisitor(
+ ReferencedProjectDeltaVisitor refProjectDv = new ReferencedProjectDeltaVisitor(
referencedJavaProject);
- delta.accept(dv);
+ delta.accept(refProjectDv);
// save the state
- mConvertToDex |= dv.needDexConvertion();
- mBuildFinalPackage |= dv.needMakeFinalPackage();
+ mConvertToDex |= refProjectDv.needDexConvertion();
+ mBuildFinalPackage |= refProjectDv.needMakeFinalPackage();
}
}
}
@@ -307,29 +299,14 @@ public class ApkBuilder extends BaseBuilder {
// At this point, we can abort the build if we have to, as we have computed
// our resource delta and stored the result.
+ abortOnBadSetup(javaProject);
- // check if we have finished loading the SDK.
- if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
- // we exit silently
- return referencedProjects;
- }
-
- // Now check the compiler compliance level, not displaying the error
- // message since this is not the first builder.
- if (ProjectHelper.checkCompilerCompliance(getProject())
- != ProjectHelper.COMPILER_COMPLIANCE_OK) {
- return referencedProjects;
- }
-
- // now check if the project has problem marker already
- if (ProjectHelper.hasError(project, true)) {
- // we found a marker with error severity: we abort the build.
- // Since this is going to happen every time we save a file while
- // errors are remaining, we do not force the display of the console, which
- // would, in most cases, show on top of the Problem view (which is more
- // important in that case).
+ if (dv != null && dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Project_Has_Errors);
+ Messages.Xml_Error);
+
+ // if there was some XML errors, we just return w/o doing
+ // anything since we've put some markers in the files anyway
return referencedProjects;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
index 534c123..04e9fbf 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
@@ -19,10 +19,13 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
+import com.android.ide.eclipse.adt.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.XmlErrorHandler;
import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -34,6 +37,8 @@ import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -841,4 +846,53 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
return oslibraryList.toArray(new String[oslibraryList.size()]);
}
+
+ /**
+ * Aborts the build if the SDK/project setups are broken. This does not
+ * display any errors.
+ *
+ * @param javaProject The {@link IJavaProject} being compiled.
+ * @throws CoreException
+ */
+ protected final void abortOnBadSetup(IJavaProject javaProject) throws CoreException {
+ // check if we have finished loading the SDK.
+ if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
+ // we exit silently
+ stopBuild("SDK is not loaded yet");
+ }
+
+ // check the compiler compliance level.
+ if (ProjectHelper.checkCompilerCompliance(getProject()) !=
+ ProjectHelper.COMPILER_COMPLIANCE_OK) {
+ // we exit silently
+ stopBuild(Messages.Compiler_Compliance_Error);
+ }
+
+ // Check that the SDK directory has been setup.
+ String osSdkFolder = AdtPlugin.getOsSdkFolder();
+
+ if (osSdkFolder == null || osSdkFolder.length() == 0) {
+ stopBuild(Messages.No_SDK_Setup_Error);
+ }
+
+ IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(javaProject.getProject());
+ if (projectTarget == null) {
+ // no target. error has been output by the container initializer:
+ // exit silently.
+ stopBuild("Project has no target");
+ }
+ }
+
+ /**
+ * Throws an exception to cancel the build.
+ *
+ * @param error the error message
+ * @param args the printf-style arguments to the error message.
+ * @throws CoreException
+ */
+ protected final void stopBuild(String error, Object... args) throws CoreException {
+ throw new CoreException(new Status(IStatus.CANCEL, AdtPlugin.PLUGIN_ID,
+ String.format(error, args)));
+ }
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 9fc4348..fd4d772 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -20,7 +20,6 @@ import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.FixLaunchConfig;
import com.android.ide.eclipse.adt.project.ProjectHelper;
-import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
@@ -224,7 +223,7 @@ public class PreCompilerBuilder extends BaseBuilder {
public PreCompilerBuilder() {
super();
}
-
+
// build() returns a list of project from which this project depends for future compilation.
@SuppressWarnings("unchecked") //$NON-NLS-1$
@Override
@@ -274,15 +273,6 @@ public class PreCompilerBuilder extends BaseBuilder {
mergeAidlFileModifications(dv.getAidlToCompile(),
dv.getAidlToRemove());
}
-
- // if there was some XML errors, we just return w/o doing
- // anything since we've put some markers in the files anyway.
- if (dv.mXmlError) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Xml_Error);
-
- return null;
- }
// get the java package from the visitor
javaPackage = dv.getManifestPackage();
@@ -295,39 +285,19 @@ public class PreCompilerBuilder extends BaseBuilder {
// At this point we have stored what needs to be build, so we can
// do some high level test and abort if needed.
-
- // check if we have finished loading the SDK.
- if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
- // we exit silently
- return null;
- }
-
- // check the compiler compliance level, not displaying the error message
- // since this is not the first builder.
- if (ProjectHelper.checkCompilerCompliance(getProject())
- != ProjectHelper.COMPILER_COMPLIANCE_OK) {
+ abortOnBadSetup(javaProject);
+
+ // if there was some XML errors, we just return w/o doing
+ // anything since we've put some markers in the files anyway.
+ if (dv != null && dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Compiler_Compliance_Error);
- return null;
- }
-
- // Check that the SDK directory has been setup.
- String osSdkFolder = AdtPlugin.getOsSdkFolder();
+ Messages.Xml_Error);
- if (osSdkFolder == null || osSdkFolder.length() == 0) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.No_SDK_Setup_Error);
- markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
- IMarker.SEVERITY_ERROR);
- return null;
- }
-
- IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
- if (projectTarget == null) {
- // no target. error has been output by the container initializer: exit silently.
- return null;
+ // This interrupts the build. The next builders will not run.
+ stopBuild(Messages.Xml_Error);
}
+
// get the manifest file
IFile manifest = AndroidManifestHelper.getManifest(project);
@@ -336,7 +306,9 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
- return null;
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
// lets check the XML of the manifest first, if that hasn't been done by the
@@ -353,7 +325,9 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(Messages.s_Contains_Xml_Error,
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
- return null;
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
// get the java package from the parser
@@ -366,7 +340,9 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
msg);
- return null;
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
// at this point we have the java package. We need to make sure it's not a different package
@@ -409,7 +385,8 @@ public class PreCompilerBuilder extends BaseBuilder {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, message);
// abort
- return null;
+ // This interrupts the build. The next builders will not run.
+ stopBuild(message);
}
@@ -430,6 +407,8 @@ public class PreCompilerBuilder extends BaseBuilder {
String osResPath = resLocation.toOSString();
String osManifestPath = manifestLocation.toOSString();
+ IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
+
// remove the aapt markers
removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT);
removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT);
@@ -517,20 +496,25 @@ public class PreCompilerBuilder extends BaseBuilder {
Messages.AAPT_Error);
// abort if exec failed.
- return null;
+ // This interrupts the build. The next builders will not run.
+ stopBuild(Messages.AAPT_Error);
}
} catch (IOException e1) {
// something happen while executing the process,
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
- return null;
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
} catch (InterruptedException e) {
// we got interrupted waiting for the process to end...
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
- return null;
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
// if the return code was OK, we refresh the folder that
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
index 1219aac..1e7b77a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
@@ -19,16 +19,20 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
+import com.android.ide.eclipse.adt.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
import java.util.Map;
@@ -36,7 +40,7 @@ import java.util.Map;
* Resource manager builder whose only purpose is to refresh the resource folder
* so that the other builder use an up to date version.
*/
-public class ResourceManagerBuilder extends IncrementalProjectBuilder {
+public class ResourceManagerBuilder extends BaseBuilder {
public static final String ID = "com.android.ide.eclipse.adt.ResourceManagerBuilder"; //$NON-NLS-1$
@@ -72,6 +76,38 @@ public class ResourceManagerBuilder extends IncrementalProjectBuilder {
BaseProjectHelper.addMarker(project, AdtConstants.MARKER_ADT, errorMessage,
IMarker.SEVERITY_ERROR);
AdtPlugin.printErrorToConsole(project, errorMessage);
+
+ // interrupt the build. The next builders will not run.
+ stopBuild(errorMessage);
+ }
+
+ // Check that the SDK directory has been setup.
+ String osSdkFolder = AdtPlugin.getOsSdkFolder();
+
+ if (osSdkFolder == null || osSdkFolder.length() == 0) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
+ Messages.No_SDK_Setup_Error);
+ markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
+ IMarker.SEVERITY_ERROR);
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(Messages.No_SDK_Setup_Error);
+ }
+
+ // check if we have finished loading the SDK.
+ IJavaProject javaProject = JavaCore.create(project);
+ if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
+ // we exit silently
+ // This interrupts the build. The next builders will not run.
+ stopBuild("SDK is not loaded yet");
+ }
+
+ // check the project has a target
+ IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
+ if (projectTarget == null) {
+ // no target. marker has been set by the container initializer: exit silently.
+ // This interrupts the build. The next builders will not run.
+ stopBuild("Project has no target");
}
// Check the preference to be sure we are supposed to refresh
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
index 2b7d01d..87f902a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
@@ -35,8 +35,8 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
-import com.android.sdklib.vm.VmManager;
-import com.android.sdklib.vm.VmManager.VmInfo;
+import com.android.sdklib.avd.AvdManager;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -74,7 +74,7 @@ import java.util.regex.Pattern;
public final class AndroidLaunchController implements IDebugBridgeChangeListener,
IDeviceChangeListener, IClientChangeListener {
- private static final String FLAG_VM = "-vm"; //$NON-NLS-1$
+ private static final String FLAG_AVD = "-avd"; //$NON-NLS-1$
private static final String FLAG_NETDELAY = "-netdelay"; //$NON-NLS-1$
private static final String FLAG_NETSPEED = "-netspeed"; //$NON-NLS-1$
private static final String FLAG_WIPE_DATA = "-wipe-data"; //$NON-NLS-1$
@@ -228,9 +228,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
public boolean mNoBootAnim = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM;
/**
- * Vm Name.
+ * AVD Name.
*/
- public String mVmName = null;
+ public String mAvdName = null;
public String mNetworkSpeed = EmulatorConfigTab.getSpeed(
LaunchConfigDelegate.DEFAULT_SPEED);
@@ -262,7 +262,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
try {
- mVmName = config.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME, mVmName);
+ mAvdName = config.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, mAvdName);
} catch (CoreException e) {
}
@@ -531,8 +531,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
- // default VM: None
- wc.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
+ // default AVD: None
+ wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String)null);
// set the default network speed
wc.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
@@ -629,12 +629,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// get the SDK
Sdk currentSdk = Sdk.getCurrent();
- VmManager vmManager = currentSdk.getVmManager();
+ AvdManager avdManager = currentSdk.getAvdManager();
// get the project target
final IAndroidTarget projectTarget = currentSdk.getTarget(project);
- // FIXME: check errors on missing sdk, vm manager, or project target.
+ // FIXME: check errors on missing sdk, AVD manager, or project target.
// device chooser response object.
final DeviceChooserResponse response = new DeviceChooserResponse();
@@ -644,81 +644,81 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* - Manually Mode
* Always display a UI that lets a user see the current running emulators/devices.
* The UI must show which devices are compatibles, and allow launching new emulators
- * with compatible (and not yet running) VM.
+ * with compatible (and not yet running) AVD.
* - Automatic Way
- * * Preferred VM set.
- * If Preferred VM is not running: launch it.
- * Launch the application on the preferred VM.
- * * No preferred VM.
+ * * Preferred AVD set.
+ * If Preferred AVD is not running: launch it.
+ * Launch the application on the preferred AVD.
+ * * No preferred AVD.
* Count the number of compatible emulators/devices.
* If != 1, display a UI similar to manual mode.
- * If == 1, launch the application on this VM/device.
+ * If == 1, launch the application on this AVD/device.
*/
if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) {
// if we are in automatic target mode, we need to find the current devices
Device[] devices = AndroidDebugBridge.getBridge().getDevices();
- // first check if we have a preferred VM name, and if it actually exists, and is valid
+ // first check if we have a preferred AVD name, and if it actually exists, and is valid
// (ie able to run the project).
- // We need to check this in case the VM was recreated with a different target that is
+ // We need to check this in case the AVD was recreated with a different target that is
// not compatible.
- VmInfo preferredVm = null;
- if (config.mVmName != null) {
- preferredVm = vmManager.getVm(config.mVmName);
- if (projectTarget.isCompatibleBaseFor(preferredVm.getTarget()) == false) {
- preferredVm = null;
+ AvdInfo preferredAvd = null;
+ if (config.mAvdName != null) {
+ preferredAvd = avdManager.getAvd(config.mAvdName);
+ if (projectTarget.isCompatibleBaseFor(preferredAvd.getTarget()) == false) {
+ preferredAvd = null;
AdtPlugin.printErrorToConsole(project, String.format(
- "Preferred VM '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible VM...",
- config.mVmName, projectTarget.getName()));
+ "Preferred AVD '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible AVD...",
+ config.mAvdName, projectTarget.getName()));
}
}
- if (preferredVm != null) {
+ if (preferredAvd != null) {
// look for a matching device
for (Device d : devices) {
- String deviceVm = d.getVmName();
- if (deviceVm != null && deviceVm.equals(config.mVmName)) {
+ String deviceAvd = d.getAvdName();
+ if (deviceAvd != null && deviceAvd.equals(config.mAvdName)) {
response.mustContinue = true;
response.mustLaunchEmulator = false;
response.deviceToUse = d;
AdtPlugin.printToConsole(project, String.format(
- "Automatic Target Mode: Preferred VM '%1$s' is available on emulator '%2$s'",
- config.mVmName, d));
+ "Automatic Target Mode: Preferred AVD '%1$s' is available on emulator '%2$s'",
+ config.mAvdName, d));
continueLaunch(response, project, launch, launchInfo, config);
return;
}
}
- // at this point we have a valid preferred VM that is not running.
+ // at this point we have a valid preferred AVD that is not running.
// We need to start it.
response.mustContinue = true;
response.mustLaunchEmulator = true;
- response.vmToLaunch = preferredVm;
+ response.avdToLaunch = preferredAvd;
AdtPlugin.printToConsole(project, String.format(
- "Automatic Target Mode: Preferred VM '%1$s' is not available. Launching new emulator.",
- config.mVmName));
+ "Automatic Target Mode: Preferred AVD '%1$s' is not available. Launching new emulator.",
+ config.mAvdName));
continueLaunch(response, project, launch, launchInfo, config);
return;
}
- // no (valid) preferred VM? look for one.
- HashMap<Device, VmInfo> compatibleRunningVms = new HashMap<Device, VmInfo>();
+ // no (valid) preferred AVD? look for one.
+ HashMap<Device, AvdInfo> compatibleRunningAvds = new HashMap<Device, AvdInfo>();
boolean hasDevice = false; // if there's 1+ device running, we may force manual mode,
// as we cannot always detect proper compatibility with
// devices. This is the case if the project target is not
// a standard platform
for (Device d : devices) {
- String deviceVm = d.getVmName();
- if (deviceVm != null) { // physical devices return null.
- VmInfo info = vmManager.getVm(deviceVm);
+ String deviceAvd = d.getAvdName();
+ if (deviceAvd != null) { // physical devices return null.
+ AvdInfo info = avdManager.getAvd(deviceAvd);
if (info != null && projectTarget.isCompatibleBaseFor(info.getTarget())) {
- compatibleRunningVms.put(d, info);
+ compatibleRunningAvds.put(d, info);
}
} else {
if (projectTarget.isPlatform()) { // means this can run on any device as long
@@ -728,7 +728,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
int apiNumber = Integer.parseInt(apiString);
if (apiNumber >= projectTarget.getApiVersionNumber()) {
// device is compatible with project
- compatibleRunningVms.put(d, null);
+ compatibleRunningAvds.put(d, null);
continue;
}
} catch (NumberFormatException e) {
@@ -741,54 +741,54 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// depending on the number of devices, we'll simulate an automatic choice
// from the device chooser or simply show up the device chooser.
- if (hasDevice == false && compatibleRunningVms.size() == 0) {
+ if (hasDevice == false && compatibleRunningAvds.size() == 0) {
// if zero emulators/devices, we launch an emulator.
- // We need to figure out which VM first.
+ // We need to figure out which AVD first.
- // we are going to take the closest VM. ie a compatible VM that has the API level
+ // we are going to take the closest AVD. ie a compatible AVD that has the API level
// closest to the project target.
- VmInfo[] vms = vmManager.getVms();
- VmInfo defaultVm = null;
- for (VmInfo vm : vms) {
- if (projectTarget.isCompatibleBaseFor(vm.getTarget())) {
- if (defaultVm == null ||
- vm.getTarget().getApiVersionNumber() <
- defaultVm.getTarget().getApiVersionNumber()) {
- defaultVm = vm;
+ AvdInfo[] avds = avdManager.getAvds();
+ AvdInfo defaultAvd = null;
+ for (AvdInfo avd : avds) {
+ if (projectTarget.isCompatibleBaseFor(avd.getTarget())) {
+ if (defaultAvd == null ||
+ avd.getTarget().getApiVersionNumber() <
+ defaultAvd.getTarget().getApiVersionNumber()) {
+ defaultAvd = avd;
}
}
}
- if (defaultVm != null) {
+ if (defaultAvd != null) {
response.mustContinue = true;
response.mustLaunchEmulator = true;
- response.vmToLaunch = defaultVm;
+ response.avdToLaunch = defaultAvd;
AdtPlugin.printToConsole(project, String.format(
- "Automatic Target Mode: launching new emulator with compatible VM '%1$s'",
- defaultVm.getName()));
+ "Automatic Target Mode: launching new emulator with compatible AVD '%1$s'",
+ defaultAvd.getName()));
continueLaunch(response, project, launch, launchInfo, config);
return;
} else {
- // FIXME: ask the user if he wants to create a VM.
- // we found no compatible VM.
+ // FIXME: ask the user if he wants to create a AVD.
+ // we found no compatible AVD.
AdtPlugin.printErrorToConsole(project, String.format(
- "Failed to find a VM compatible with target '%1$s'. Launch aborted.",
+ "Failed to find a AVD compatible with target '%1$s'. Launch aborted.",
projectTarget.getName()));
launch.stopLaunch();
return;
}
- } else if (hasDevice == false && compatibleRunningVms.size() == 1) {
- Entry<Device, VmInfo> e = compatibleRunningVms.entrySet().iterator().next();
+ } else if (hasDevice == false && compatibleRunningAvds.size() == 1) {
+ Entry<Device, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next();
response.mustContinue = true;
response.mustLaunchEmulator = false;
response.deviceToUse = e.getKey();
- // get the VmInfo, if null, the device is a physical device.
- VmInfo vmInfo = e.getValue();
- if (vmInfo != null) {
- message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible VM '%2$s'",
+ // get the AvdInfo, if null, the device is a physical device.
+ AvdInfo avdInfo = e.getValue();
+ if (avdInfo != null) {
+ message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible AVD '%2$s'",
response.deviceToUse, e.getValue().getName());
} else {
message = String.format("Automatic Target Mode: using device '%1$s'",
@@ -801,7 +801,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
// if more than one device, we'll bring up the DeviceChooser dialog below.
- if (compatibleRunningVms.size() >= 2) {
+ if (compatibleRunningAvds.size() >= 2) {
message = "Automatic Target Mode: Several compatible targets. Please select a target device.";
} else if (hasDevice) {
message = "Automatic Target Mode: Unable to detect device compatibility. Please select a target device.";
@@ -849,7 +849,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
synchronized (sListLock) {
mWaitingForEmulatorLaunches.add(launchInfo);
AdtPlugin.printToConsole(project, "Launching a new emulator.");
- boolean status = launchEmulator(config, response.vmToLaunch);
+ boolean status = launchEmulator(config, response.avdToLaunch);
if (status == false) {
// launching the emulator failed!
@@ -1323,7 +1323,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
}
- private boolean launchEmulator(AndroidLaunchConfiguration config, VmInfo vmToLaunch) {
+ private boolean launchEmulator(AndroidLaunchConfiguration config, AvdInfo avdToLaunch) {
// split the custom command line in segments
ArrayList<String> customArgs = new ArrayList<String>();
@@ -1353,8 +1353,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
ArrayList<String> list = new ArrayList<String>();
list.add(AdtPlugin.getOsAbsoluteEmulator());
- list.add(FLAG_VM);
- list.add(vmToLaunch.getName());
+ list.add(FLAG_AVD);
+ list.add(avdToLaunch.getName());
if (config.mNetworkSpeed != null) {
list.add(FLAG_NETSPEED);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
index 19ec9a7..05bc171 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
@@ -31,7 +31,7 @@ import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.Delay
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.vm.VmManager.VmInfo;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -70,10 +70,10 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
private final static int ICON_WIDTH = 16;
private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
- private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
- private final static String PREFS_COL_VM = "deviceChooser.vm"; //$NON-NLS-1$
+ private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
+ private final static String PREFS_COL_AVD = "deviceChooser.avd"; //$NON-NLS-1$
private final static String PREFS_COL_TARGET = "deviceChooser.target"; //$NON-NLS-1$
- private final static String PREFS_COL_DEBUG = "deviceChooser.debug"; //$NON-NLS-1$
+ private final static String PREFS_COL_DEBUG = "deviceChooser.debug"; //$NON-NLS-1$
private Table mDeviceTable;
private TableViewer mViewer;
@@ -149,8 +149,8 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
return mNoMatchImage;
}
} else {
- // get the VmInfo
- VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
+ // get the AvdInfo
+ AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName());
if (info == null) {
return mWarningImage;
}
@@ -171,13 +171,13 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
return device.getSerialNumber();
case 1:
if (device.isEmulator()) {
- return device.getVmName();
+ return device.getAvdName();
} else {
- return "N/A"; // devices don't have VM names.
+ return "N/A"; // devices don't have AVD names.
}
case 2:
if (device.isEmulator()) {
- VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
+ AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName());
if (info == null) {
return "?";
}
@@ -221,7 +221,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
public static class DeviceChooserResponse {
public boolean mustContinue;
public boolean mustLaunchEmulator;
- public VmInfo vmToLaunch;
+ public AvdInfo avdToLaunch;
public Device deviceToUse;
}
@@ -314,9 +314,9 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
PREFS_COL_SERIAL, store);
- TableHelper.createTableColumn(mDeviceTable, "VM Name",
+ TableHelper.createTableColumn(mDeviceTable, "AVD Name",
SWT.LEFT, "engineering", //$NON-NLS-1$
- PREFS_COL_VM, store);
+ PREFS_COL_AVD, store);
TableHelper.createTableColumn(mDeviceTable, "Target",
SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
index 68deec3..bbd320b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
@@ -80,7 +80,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
*/
public static final String ATTR_ACTIVITY = AdtPlugin.PLUGIN_ID + ".activity"; //$NON-NLS-1$
- public static final String ATTR_VM_NAME = AdtPlugin.PLUGIN_ID + ".vm"; //$NON-NLS-1$
+ public static final String ATTR_AVD_NAME = AdtPlugin.PLUGIN_ID + ".avd"; //$NON-NLS-1$
public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
index f4f5281..a581e5c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
@@ -22,9 +22,9 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.vm.VmManager;
-import com.android.sdklib.vm.VmManager.VmInfo;
-import com.android.sdkuilib.VmSelector;
+import com.android.sdklib.avd.AvdManager;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
+import com.android.sdkuilib.AvdSelector;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
@@ -75,7 +75,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
private Button mAutoTargetButton;
private Button mManualTargetButton;
- private VmSelector mPreferredVmSelector;
+ private AvdSelector mPreferredAvdSelector;
private Combo mSpeedCombo;
@@ -163,11 +163,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
}
});
- new Label(targetModeGroup, SWT.NONE).setText("Preferred VM");
- VmInfo[] vms = new VmInfo[0];
- mPreferredVmSelector = new VmSelector(targetModeGroup, vms,
+ new Label(targetModeGroup, SWT.NONE).setText("Preferred Android Virtual Device");
+ AvdInfo[] avds = new AvdInfo[0];
+ mPreferredAvdSelector = new AvdSelector(targetModeGroup, avds,
false /*allowMultipleSelection*/);
- mPreferredVmSelector.setSelectionListener(new SelectionAdapter() {
+ mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateLaunchConfigurationDialog();
@@ -277,7 +277,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void initializeFrom(ILaunchConfiguration configuration) {
- VmManager vmManager = Sdk.getCurrent().getVmManager();
+ AvdManager avdManager = Sdk.getCurrent().getAvdManager();
boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic
try {
@@ -311,34 +311,34 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
}
}
- // update the VM list
- VmInfo[] vms = null;
- if (vmManager != null) {
- vms = vmManager.getVms();
+ // update the AVD list
+ AvdInfo[] avds = null;
+ if (avdManager != null) {
+ avds = avdManager.getAvds();
}
IAndroidTarget projectTarget = null;
if (project != null) {
projectTarget = Sdk.getCurrent().getTarget(project);
} else {
- vms = null; // no project? we don't want to display any "compatible" VMs.
+ avds = null; // no project? we don't want to display any "compatible" AVDs.
}
- mPreferredVmSelector.setVms(vms, projectTarget);
+ mPreferredAvdSelector.setAvds(avds, projectTarget);
stringValue = "";
try {
- stringValue = configuration.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME,
+ stringValue = configuration.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME,
stringValue);
} catch (CoreException e) {
// let's not do anything here, we'll use the default value
}
- if (stringValue != null && stringValue.length() > 0 && vmManager != null) {
- VmInfo targetVm = vmManager.getVm(stringValue);
- mPreferredVmSelector.setSelection(targetVm);
+ if (stringValue != null && stringValue.length() > 0 && avdManager != null) {
+ AvdInfo targetAvd = avdManager.getAvd(stringValue);
+ mPreferredAvdSelector.setSelection(targetAvd);
} else {
- mPreferredVmSelector.setSelection(null);
+ mPreferredAvdSelector.setSelection(null);
}
value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
@@ -404,11 +404,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
mAutoTargetButton.getSelection());
- VmInfo vm = mPreferredVmSelector.getFirstSelected();
- if (vm != null) {
- configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, vm.getName());
+ AvdInfo avd = mPreferredAvdSelector.getFirstSelected();
+ if (avd != null) {
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, avd.getName());
} else {
- configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String)null);
}
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
mSpeedCombo.getSelectionIndex());
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
index 8678923..cbeddd7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
@@ -361,7 +361,7 @@ public final class ProjectHelper {
}
/**
- * Returns a {@link IProject} by its running application name, as it returned by the VM.
+ * Returns a {@link IProject} by its running application name, as it returned by the AVD.
* <p/>
* <var>applicationName</var> will in most case be the package declared in the manifest, but
* can, in some cases, be a custom process name declared in the manifest, in the
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
index 172b4ae..01b722f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
@@ -23,9 +23,9 @@ import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
+import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.project.ProjectProperties;
import com.android.sdklib.project.ProjectProperties.PropertyType;
-import com.android.sdklib.vm.VmManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
@@ -52,7 +52,7 @@ public class Sdk {
private static Sdk sCurrentSdk = null;
private final SdkManager mManager;
- private final VmManager mVmManager;
+ private final AvdManager mAvdManager;
private final HashMap<IProject, IAndroidTarget> mProjectMap =
new HashMap<IProject, IAndroidTarget>();
@@ -95,13 +95,13 @@ public class Sdk {
// get an SdkManager object for the location
SdkManager manager = SdkManager.createManager(sdkLocation, log);
if (manager != null) {
- VmManager vmManager = null;
+ AvdManager avdManager = null;
try {
- vmManager = new VmManager(manager, log);
+ avdManager = new AvdManager(manager, log);
} catch (AndroidLocationException e) {
- log.error(e, "Error parsing the VMs");
+ log.error(e, "Error parsing the AVDs");
}
- sCurrentSdk = new Sdk(manager, vmManager);
+ sCurrentSdk = new Sdk(manager, avdManager);
return sCurrentSdk;
} else {
StringBuilder sb = new StringBuilder("Error Loading the SDK:\n");
@@ -255,16 +255,16 @@ public class Sdk {
}
/**
- * Returns the {@link VmManager}. If the VmManager failed to parse the VM folder, this could
+ * Returns the {@link AvdManager}. If the AvdManager failed to parse the AVD folder, this could
* be <code>null</code>.
*/
- public VmManager getVmManager() {
- return mVmManager;
+ public AvdManager getAvdManager() {
+ return mAvdManager;
}
- private Sdk(SdkManager manager, VmManager vmManager) {
+ private Sdk(SdkManager manager, AvdManager avdManager) {
mManager = manager;
- mVmManager = vmManager;
+ mAvdManager = avdManager;
// pre-compute some paths
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java
index 0f388f4..d5ee2ca 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java
@@ -187,10 +187,10 @@ public final class CustomViewDescriptorService {
/**
* Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
*
- * @param type
+ * @param type
* @param project
* @param typeHierarchy
- * @return A ViewElementDescriptor
+ * @return A ViewElementDescriptor or null if type or typeHierarchy is null.
*/
private ViewElementDescriptor getDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
@@ -198,12 +198,17 @@ public final class CustomViewDescriptorService {
List<ElementDescriptor> builtInList = null;
Sdk currentSdk = Sdk.getCurrent();
- IAndroidTarget target = currentSdk.getTarget(project);
+ IAndroidTarget target = currentSdk == null ? null : currentSdk.getTarget(project);
if (target != null) {
AndroidTargetData data = currentSdk.getTargetData(target);
builtInList = data.getLayoutDescriptors().getViewDescriptors();
}
+ // give up if there's no type
+ if (type == null) {
+ return null;
+ }
+
String canonicalName = type.getFullyQualifiedName();
if (builtInList != null) {
@@ -218,6 +223,11 @@ public final class CustomViewDescriptorService {
}
// it's not a built-in class? Lets look if the superclass is built-in
+ // give up if there's no type
+ if (typeHierarchy == null) {
+ return null;
+ }
+
IType parentType = typeHierarchy.getSuperclass(type);
if (parentType != null) {
ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java
index 5908574..4a9fbb1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java
@@ -79,7 +79,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode
public void updateValue(Node xml_attribute_node) {
mCurrentValue = DEFAULT_VALUE;
if (xml_attribute_node != null) {
- mCurrentValue = xml_attribute_node.getNodeValue().trim();
+ mCurrentValue = xml_attribute_node.getNodeValue();
}
if (isValid() && !getTextWidgetValue().equals(mCurrentValue)) {
@@ -101,7 +101,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode
public void commit() {
UiElementNode parent = getUiParent();
if (parent != null && isValid() && isDirty()) {
- String value = getTextWidgetValue().trim();
+ String value = getTextWidgetValue();
if (parent.commitAttributeToXml(this, value)) {
mCurrentValue = value;
setDirty(false);
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
index 5a89d01..8c52d81 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
@@ -22,7 +22,6 @@ import com.android.ide.eclipse.mock.JavaProjectMock;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.launching.JavaRuntime;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
index 872938b..8af7e02 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
@@ -28,7 +28,7 @@ import java.util.HashMap;
import junit.framework.TestCase;
/**
- * Unit Test for {@link FrameworkClassLoader}.
+ * Unit Test for {@link AndroidJarLoader}.
*
* Uses the classes jar.example.Class1/Class2 stored in tests/data/jar_example.jar.
*/
@@ -36,7 +36,7 @@ public class AndroidJarLoaderTest extends TestCase {
private AndroidJarLoader mFrameworkClassLoader;
- /** Creates an instance of {@link FrameworkClassLoader} on our test data JAR */
+ /** Creates an instance of {@link AndroidJarLoader} on our test data JAR */
@Override
public void setUp() throws Exception {
String jarfilePath = AdtTestData.getInstance().getTestFilePath("jar_example.jar"); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java
index b66fcd6..cedf4d4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java
@@ -35,7 +35,7 @@ import junit.framework.TestCase;
* Test the inner private methods of PlatformDataParser.
*
* Convention: method names that start with an underscore are actually local wrappers
- * that call private methods from {@link FrameworkResourceParser} using reflection.
+ * that call private methods from {@link AndroidTargetParser} using reflection.
* This is inspired by the Python coding rule which mandates underscores prefixes for
* "private" methods.
*/
@@ -131,6 +131,7 @@ public class LayoutParamsParserTest extends TestCase {
//---- access to private methods
/** Calls the private constructor of the parser */
+ @SuppressWarnings("unused")
private AndroidTargetParser _Constructor(String osJarPath) throws Exception {
Constructor<AndroidTargetParser> constructor =
AndroidTargetParser.class.getDeclaredConstructor(String.class);
@@ -139,6 +140,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private getLayoutClasses() of the parser */
+ @SuppressWarnings("unused")
private void _getLayoutClasses() throws Exception {
Method method = AndroidTargetParser.class.getDeclaredMethod("getLayoutClasses"); //$NON-NLS-1$
method.setAccessible(true);
@@ -146,6 +148,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private addGroup() of the parser */
+ @SuppressWarnings("unused")
private ViewClassInfo _addGroup(Class<?> groupClass) throws Exception {
Method method = LayoutParamsParser.class.getDeclaredMethod("addGroup", //$NON-NLS-1$
IClassDescriptor.class);
@@ -154,6 +157,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private addLayoutParams() of the parser */
+ @SuppressWarnings("unused")
private LayoutParamsInfo _addLayoutParams(Class<?> groupClass) throws Exception {
Method method = LayoutParamsParser.class.getDeclaredMethod("addLayoutParams", //$NON-NLS-1$
IClassDescriptor.class);
diff --git a/scripts/app_engine_server/LICENSE b/scripts/app_engine_server/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/scripts/app_engine_server/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/scripts/app_engine_server/app.yaml b/scripts/app_engine_server/app.yaml
new file mode 100755
index 0000000..1fb50c7
--- /dev/null
+++ b/scripts/app_engine_server/app.yaml
@@ -0,0 +1,16 @@
+application: androidappdocs-staging
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /gae_shell/static
+ static_dir: gae_shell/static
+ expiration: 1d
+
+- url: /gae_shell/.*
+ script: /gae_shell/shell.py
+ login: admin
+
+- url: .*
+ script: main.py
diff --git a/scripts/app_engine_server/gae_shell/README b/scripts/app_engine_server/gae_shell/README
new file mode 100644
index 0000000..5b0089f
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/README
@@ -0,0 +1,17 @@
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
diff --git a/scripts/app_engine_server/gae_shell/__init__.py b/scripts/app_engine_server/gae_shell/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/__init__.py
diff --git a/scripts/app_engine_server/gae_shell/__init__.pyc b/scripts/app_engine_server/gae_shell/__init__.pyc
new file mode 100644
index 0000000..84951e9
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/__init__.pyc
Binary files differ
diff --git a/scripts/app_engine_server/gae_shell/shell.py b/scripts/app_engine_server/gae_shell/shell.py
new file mode 100755
index 0000000..df2fb17
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/shell.py
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+#
+# Copyright 2007 Google Inc.
+#
+# 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.
+
+"""
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
+
+TODO: unit tests!
+"""
+
+import logging
+import new
+import os
+import pickle
+import sys
+import traceback
+import types
+import wsgiref.handlers
+
+from google.appengine.api import users
+from google.appengine.ext import db
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+
+# Set to True if stack traces should be shown in the browser, etc.
+_DEBUG = True
+
+# The entity kind for shell sessions. Feel free to rename to suit your app.
+_SESSION_KIND = '_Shell_Session'
+
+# Types that can't be pickled.
+UNPICKLABLE_TYPES = (
+ types.ModuleType,
+ types.TypeType,
+ types.ClassType,
+ types.FunctionType,
+ )
+
+# Unpicklable statements to seed new sessions with.
+INITIAL_UNPICKLABLES = [
+ 'import logging',
+ 'import os',
+ 'import sys',
+ 'from google.appengine.ext import db',
+ 'from google.appengine.api import users',
+ ]
+
+
+class Session(db.Model):
+ """A shell session. Stores the session's globals.
+
+ Each session globals is stored in one of two places:
+
+ If the global is picklable, it's stored in the parallel globals and
+ global_names list properties. (They're parallel lists to work around the
+ unfortunate fact that the datastore can't store dictionaries natively.)
+
+ If the global is not picklable (e.g. modules, classes, and functions), or if
+ it was created by the same statement that created an unpicklable global,
+ it's not stored directly. Instead, the statement is stored in the
+ unpicklables list property. On each request, before executing the current
+ statement, the unpicklable statements are evaluated to recreate the
+ unpicklable globals.
+
+ The unpicklable_names property stores all of the names of globals that were
+ added by unpicklable statements. When we pickle and store the globals after
+ executing a statement, we skip the ones in unpicklable_names.
+
+ Using Text instead of string is an optimization. We don't query on any of
+ these properties, so they don't need to be indexed.
+ """
+ global_names = db.ListProperty(db.Text)
+ globals = db.ListProperty(db.Blob)
+ unpicklable_names = db.ListProperty(db.Text)
+ unpicklables = db.ListProperty(db.Text)
+
+ def set_global(self, name, value):
+ """Adds a global, or updates it if it already exists.
+
+ Also removes the global from the list of unpicklable names.
+
+ Args:
+ name: the name of the global to remove
+ value: any picklable value
+ """
+ blob = db.Blob(pickle.dumps(value))
+
+ if name in self.global_names:
+ index = self.global_names.index(name)
+ self.globals[index] = blob
+ else:
+ self.global_names.append(db.Text(name))
+ self.globals.append(blob)
+
+ self.remove_unpicklable_name(name)
+
+ def remove_global(self, name):
+ """Removes a global, if it exists.
+
+ Args:
+ name: string, the name of the global to remove
+ """
+ if name in self.global_names:
+ index = self.global_names.index(name)
+ del self.global_names[index]
+ del self.globals[index]
+
+ def globals_dict(self):
+ """Returns a dictionary view of the globals.
+ """
+ return dict((name, pickle.loads(val))
+ for name, val in zip(self.global_names, self.globals))
+
+ def add_unpicklable(self, statement, names):
+ """Adds a statement and list of names to the unpicklables.
+
+ Also removes the names from the globals.
+
+ Args:
+ statement: string, the statement that created new unpicklable global(s).
+ names: list of strings; the names of the globals created by the statement.
+ """
+ self.unpicklables.append(db.Text(statement))
+
+ for name in names:
+ self.remove_global(name)
+ if name not in self.unpicklable_names:
+ self.unpicklable_names.append(db.Text(name))
+
+ def remove_unpicklable_name(self, name):
+ """Removes a name from the list of unpicklable names, if it exists.
+
+ Args:
+ name: string, the name of the unpicklable global to remove
+ """
+ if name in self.unpicklable_names:
+ self.unpicklable_names.remove(name)
+
+
+class FrontPageHandler(webapp.RequestHandler):
+ """Creates a new session and renders the shell.html template.
+ """
+
+ def get(self):
+ # set up the session. TODO: garbage collect old shell sessions
+ session_key = self.request.get('session')
+ if session_key:
+ session = Session.get(session_key)
+ else:
+ # create a new session
+ session = Session()
+ session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
+ session_key = session.put()
+
+ template_file = os.path.join(os.path.dirname(__file__), 'templates',
+ 'shell.html')
+ session_url = '/?session=%s' % session_key
+ vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
+ 'python_version': sys.version,
+ 'session': str(session_key),
+ 'user': users.get_current_user(),
+ 'login_url': users.create_login_url(session_url),
+ 'logout_url': users.create_logout_url(session_url),
+ }
+ rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
+ self.response.out.write(rendered)
+
+
+class StatementHandler(webapp.RequestHandler):
+ """Evaluates a python statement in a given session and returns the result.
+ """
+
+ def get(self):
+ self.response.headers['Content-Type'] = 'text/plain'
+
+ # extract the statement to be run
+ statement = self.request.get('statement')
+ if not statement:
+ return
+
+ # the python compiler doesn't like network line endings
+ statement = statement.replace('\r\n', '\n')
+
+ # add a couple newlines at the end of the statement. this makes
+ # single-line expressions such as 'class Foo: pass' evaluate happily.
+ statement += '\n\n'
+
+ # log and compile the statement up front
+ try:
+ logging.info('Compiling and evaluating:\n%s' % statement)
+ compiled = compile(statement, '<string>', 'single')
+ except:
+ self.response.out.write(traceback.format_exc())
+ return
+
+ # create a dedicated module to be used as this statement's __main__
+ statement_module = new.module('__main__')
+
+ # use this request's __builtin__, since it changes on each request.
+ # this is needed for import statements, among other things.
+ import __builtin__
+ statement_module.__builtins__ = __builtin__
+
+ # load the session from the datastore
+ session = Session.get(self.request.get('session'))
+
+ # swap in our custom module for __main__. then unpickle the session
+ # globals, run the statement, and re-pickle the session globals, all
+ # inside it.
+ old_main = sys.modules.get('__main__')
+ try:
+ sys.modules['__main__'] = statement_module
+ statement_module.__name__ = '__main__'
+
+ # re-evaluate the unpicklables
+ for code in session.unpicklables:
+ exec code in statement_module.__dict__
+
+ # re-initialize the globals
+ for name, val in session.globals_dict().items():
+ try:
+ statement_module.__dict__[name] = val
+ except:
+ msg = 'Dropping %s since it could not be unpickled.\n' % name
+ self.response.out.write(msg)
+ logging.warning(msg + traceback.format_exc())
+ session.remove_global(name)
+
+ # run!
+ old_globals = dict(statement_module.__dict__)
+ try:
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ try:
+ sys.stdout = self.response.out
+ sys.stderr = self.response.out
+ exec compiled in statement_module.__dict__
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ except:
+ self.response.out.write(traceback.format_exc())
+ return
+
+ # extract the new globals that this statement added
+ new_globals = {}
+ for name, val in statement_module.__dict__.items():
+ if name not in old_globals or val != old_globals[name]:
+ new_globals[name] = val
+
+ if True in [isinstance(val, UNPICKLABLE_TYPES)
+ for val in new_globals.values()]:
+ # this statement added an unpicklable global. store the statement and
+ # the names of all of the globals it added in the unpicklables.
+ session.add_unpicklable(statement, new_globals.keys())
+ logging.debug('Storing this statement as an unpicklable.')
+
+ else:
+ # this statement didn't add any unpicklables. pickle and store the
+ # new globals back into the datastore.
+ for name, val in new_globals.items():
+ if not name.startswith('__'):
+ session.set_global(name, val)
+
+ finally:
+ sys.modules['__main__'] = old_main
+
+ session.put()
+
+
+def main():
+ application = webapp.WSGIApplication(
+ [('/gae_shell/', FrontPageHandler),
+ ('/gae_shell/shell.do', StatementHandler)], debug=_DEBUG)
+ wsgiref.handlers.CGIHandler().run(application)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/app_engine_server/gae_shell/shell.py~ b/scripts/app_engine_server/gae_shell/shell.py~
new file mode 100755
index 0000000..dee9fdb
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/shell.py~
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+#
+# Copyright 2007 Google Inc.
+#
+# 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.
+
+"""
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
+
+TODO: unit tests!
+"""
+
+import logging
+import new
+import os
+import pickle
+import sys
+import traceback
+import types
+import wsgiref.handlers
+
+from google.appengine.api import users
+from google.appengine.ext import db
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+
+# Set to True if stack traces should be shown in the browser, etc.
+_DEBUG = True
+
+# The entity kind for shell sessions. Feel free to rename to suit your app.
+_SESSION_KIND = '_Shell_Session'
+
+# Types that can't be pickled.
+UNPICKLABLE_TYPES = (
+ types.ModuleType,
+ types.TypeType,
+ types.ClassType,
+ types.FunctionType,
+ )
+
+# Unpicklable statements to seed new sessions with.
+INITIAL_UNPICKLABLES = [
+ 'import logging',
+ 'import os',
+ 'import sys',
+ 'from google.appengine.ext import db',
+ 'from google.appengine.api import users',
+ ]
+
+
+class Session(db.Model):
+ """A shell session. Stores the session's globals.
+
+ Each session globals is stored in one of two places:
+
+ If the global is picklable, it's stored in the parallel globals and
+ global_names list properties. (They're parallel lists to work around the
+ unfortunate fact that the datastore can't store dictionaries natively.)
+
+ If the global is not picklable (e.g. modules, classes, and functions), or if
+ it was created by the same statement that created an unpicklable global,
+ it's not stored directly. Instead, the statement is stored in the
+ unpicklables list property. On each request, before executing the current
+ statement, the unpicklable statements are evaluated to recreate the
+ unpicklable globals.
+
+ The unpicklable_names property stores all of the names of globals that were
+ added by unpicklable statements. When we pickle and store the globals after
+ executing a statement, we skip the ones in unpicklable_names.
+
+ Using Text instead of string is an optimization. We don't query on any of
+ these properties, so they don't need to be indexed.
+ """
+ global_names = db.ListProperty(db.Text)
+ globals = db.ListProperty(db.Blob)
+ unpicklable_names = db.ListProperty(db.Text)
+ unpicklables = db.ListProperty(db.Text)
+
+ def set_global(self, name, value):
+ """Adds a global, or updates it if it already exists.
+
+ Also removes the global from the list of unpicklable names.
+
+ Args:
+ name: the name of the global to remove
+ value: any picklable value
+ """
+ blob = db.Blob(pickle.dumps(value))
+
+ if name in self.global_names:
+ index = self.global_names.index(name)
+ self.globals[index] = blob
+ else:
+ self.global_names.append(db.Text(name))
+ self.globals.append(blob)
+
+ self.remove_unpicklable_name(name)
+
+ def remove_global(self, name):
+ """Removes a global, if it exists.
+
+ Args:
+ name: string, the name of the global to remove
+ """
+ if name in self.global_names:
+ index = self.global_names.index(name)
+ del self.global_names[index]
+ del self.globals[index]
+
+ def globals_dict(self):
+ """Returns a dictionary view of the globals.
+ """
+ return dict((name, pickle.loads(val))
+ for name, val in zip(self.global_names, self.globals))
+
+ def add_unpicklable(self, statement, names):
+ """Adds a statement and list of names to the unpicklables.
+
+ Also removes the names from the globals.
+
+ Args:
+ statement: string, the statement that created new unpicklable global(s).
+ names: list of strings; the names of the globals created by the statement.
+ """
+ self.unpicklables.append(db.Text(statement))
+
+ for name in names:
+ self.remove_global(name)
+ if name not in self.unpicklable_names:
+ self.unpicklable_names.append(db.Text(name))
+
+ def remove_unpicklable_name(self, name):
+ """Removes a name from the list of unpicklable names, if it exists.
+
+ Args:
+ name: string, the name of the unpicklable global to remove
+ """
+ if name in self.unpicklable_names:
+ self.unpicklable_names.remove(name)
+
+
+class FrontPageHandler(webapp.RequestHandler):
+ """Creates a new session and renders the shell.html template.
+ """
+
+ def get(self):
+ # set up the session. TODO: garbage collect old shell sessions
+ session_key = self.request.get('session')
+ if session_key:
+ session = Session.get(session_key)
+ else:
+ # create a new session
+ session = Session()
+ session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
+ session_key = session.put()
+
+ template_file = os.path.join(os.path.dirname(__file__), 'templates',
+ 'shell.html')
+ session_url = '/?session=%s' % session_key
+ vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
+ 'python_version': sys.version,
+ 'session': str(session_key),
+ 'user': users.get_current_user(),
+ 'login_url': users.create_login_url(session_url),
+ 'logout_url': users.create_logout_url(session_url),
+ }
+ rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
+ self.response.out.write(rendered)
+
+
+class StatementHandler(webapp.RequestHandler):
+ """Evaluates a python statement in a given session and returns the result.
+ """
+
+ def get(self):
+ self.response.headers['Content-Type'] = 'text/plain'
+
+ # extract the statement to be run
+ statement = self.request.get('statement')
+ if not statement:
+ return
+
+ # the python compiler doesn't like network line endings
+ statement = statement.replace('\r\n', '\n')
+
+ # add a couple newlines at the end of the statement. this makes
+ # single-line expressions such as 'class Foo: pass' evaluate happily.
+ statement += '\n\n'
+
+ # log and compile the statement up front
+ try:
+ logging.info('Compiling and evaluating:\n%s' % statement)
+ compiled = compile(statement, '<string>', 'single')
+ except:
+ self.response.out.write(traceback.format_exc())
+ return
+
+ # create a dedicated module to be used as this statement's __main__
+ statement_module = new.module('__main__')
+
+ # use this request's __builtin__, since it changes on each request.
+ # this is needed for import statements, among other things.
+ import __builtin__
+ statement_module.__builtins__ = __builtin__
+
+ # load the session from the datastore
+ session = Session.get(self.request.get('session'))
+
+ # swap in our custom module for __main__. then unpickle the session
+ # globals, run the statement, and re-pickle the session globals, all
+ # inside it.
+ old_main = sys.modules.get('__main__')
+ try:
+ sys.modules['__main__'] = statement_module
+ statement_module.__name__ = '__main__'
+
+ # re-evaluate the unpicklables
+ for code in session.unpicklables:
+ exec code in statement_module.__dict__
+
+ # re-initialize the globals
+ for name, val in session.globals_dict().items():
+ try:
+ statement_module.__dict__[name] = val
+ except:
+ msg = 'Dropping %s since it could not be unpickled.\n' % name
+ self.response.out.write(msg)
+ logging.warning(msg + traceback.format_exc())
+ session.remove_global(name)
+
+ # run!
+ old_globals = dict(statement_module.__dict__)
+ try:
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ try:
+ sys.stdout = self.response.out
+ sys.stderr = self.response.out
+ exec compiled in statement_module.__dict__
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ except:
+ self.response.out.write(traceback.format_exc())
+ return
+
+ # extract the new globals that this statement added
+ new_globals = {}
+ for name, val in statement_module.__dict__.items():
+ if name not in old_globals or val != old_globals[name]:
+ new_globals[name] = val
+
+ if True in [isinstance(val, UNPICKLABLE_TYPES)
+ for val in new_globals.values()]:
+ # this statement added an unpicklable global. store the statement and
+ # the names of all of the globals it added in the unpicklables.
+ session.add_unpicklable(statement, new_globals.keys())
+ logging.debug('Storing this statement as an unpicklable.')
+
+ else:
+ # this statement didn't add any unpicklables. pickle and store the
+ # new globals back into the datastore.
+ for name, val in new_globals.items():
+ if not name.startswith('__'):
+ session.set_global(name, val)
+
+ finally:
+ sys.modules['__main__'] = old_main
+
+ session.put()
+
+
+def main():
+ application = webapp.WSGIApplication(
+ [('/', FrontPageHandler),
+ ('/shell.do', StatementHandler)], debug=_DEBUG)
+ wsgiref.handlers.CGIHandler().run(application)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/app_engine_server/gae_shell/static/shell.js b/scripts/app_engine_server/gae_shell/static/shell.js
new file mode 100644
index 0000000..4aa1583
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/static/shell.js
@@ -0,0 +1,195 @@
+// Copyright 2007 Google Inc.
+//
+// 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.
+
+/**
+ * @fileoverview
+ * Javascript code for the interactive AJAX shell.
+ *
+ * Part of http://code.google.com/p/google-app-engine-samples/.
+ *
+ * Includes a function (shell.runStatement) that sends the current python
+ * statement in the shell prompt text box to the server, and a callback
+ * (shell.done) that displays the results when the XmlHttpRequest returns.
+ *
+ * Also includes cross-browser code (shell.getXmlHttpRequest) to get an
+ * XmlHttpRequest.
+ */
+
+/**
+ * Shell namespace.
+ * @type {Object}
+ */
+var shell = {}
+
+/**
+ * The shell history. history is an array of strings, ordered oldest to
+ * newest. historyCursor is the current history element that the user is on.
+ *
+ * The last history element is the statement that the user is currently
+ * typing. When a statement is run, it's frozen in the history, a new history
+ * element is added to the end of the array for the new statement, and
+ * historyCursor is updated to point to the new element.
+ *
+ * @type {Array}
+ */
+shell.history = [''];
+
+/**
+ * See {shell.history}
+ * @type {number}
+ */
+shell.historyCursor = 0;
+
+/**
+ * A constant for the XmlHttpRequest 'done' state.
+ * @type Number
+ */
+shell.DONE_STATE = 4;
+
+/**
+ * A cross-browser function to get an XmlHttpRequest object.
+ *
+ * @return {XmlHttpRequest?} a new XmlHttpRequest
+ */
+shell.getXmlHttpRequest = function() {
+ if (window.XMLHttpRequest) {
+ return new XMLHttpRequest();
+ } else if (window.ActiveXObject) {
+ try {
+ return new ActiveXObject('Msxml2.XMLHTTP');
+ } catch(e) {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ }
+
+ return null;
+};
+
+/**
+ * This is the prompt textarea's onkeypress handler. Depending on the key that
+ * was pressed, it will run the statement, navigate the history, or update the
+ * current statement in the history.
+ *
+ * @param {Event} event the keypress event
+ * @return {Boolean} false to tell the browser not to submit the form.
+ */
+shell.onPromptKeyPress = function(event) {
+ var statement = document.getElementById('statement');
+
+ if (this.historyCursor == this.history.length - 1) {
+ // we're on the current statement. update it in the history before doing
+ // anything.
+ this.history[this.historyCursor] = statement.value;
+ }
+
+ // should we pull something from the history?
+ if (event.ctrlKey && event.keyCode == 38 /* up arrow */) {
+ if (this.historyCursor > 0) {
+ statement.value = this.history[--this.historyCursor];
+ }
+ return false;
+ } else if (event.ctrlKey && event.keyCode == 40 /* down arrow */) {
+ if (this.historyCursor < this.history.length - 1) {
+ statement.value = this.history[++this.historyCursor];
+ }
+ return false;
+ } else if (!event.altKey) {
+ // probably changing the statement. update it in the history.
+ this.historyCursor = this.history.length - 1;
+ this.history[this.historyCursor] = statement.value;
+ }
+
+ // should we submit?
+ var ctrlEnter = (document.getElementById('submit_key').value == 'ctrl-enter');
+ if (event.keyCode == 13 /* enter */ && !event.altKey && !event.shiftKey &&
+ event.ctrlKey == ctrlEnter) {
+ return this.runStatement();
+ }
+};
+
+/**
+ * The XmlHttpRequest callback. If the request succeeds, it adds the command
+ * and its resulting output to the shell history div.
+ *
+ * @param {XmlHttpRequest} req the XmlHttpRequest we used to send the current
+ * statement to the server
+ */
+shell.done = function(req) {
+ if (req.readyState == this.DONE_STATE) {
+ var statement = document.getElementById('statement')
+ statement.className = 'prompt';
+
+ // add the command to the shell output
+ var output = document.getElementById('output');
+
+ output.value += '\n>>> ' + statement.value;
+ statement.value = '';
+
+ // add a new history element
+ this.history.push('');
+ this.historyCursor = this.history.length - 1;
+
+ // add the command's result
+ var result = req.responseText.replace(/^\s*|\s*$/g, ''); // trim whitespace
+ if (result != '')
+ output.value += '\n' + result;
+
+ // scroll to the bottom
+ output.scrollTop = output.scrollHeight;
+ if (output.createTextRange) {
+ var range = output.createTextRange();
+ range.collapse(false);
+ range.select();
+ }
+ }
+};
+
+/**
+ * This is the form's onsubmit handler. It sends the python statement to the
+ * server, and registers shell.done() as the callback to run when it returns.
+ *
+ * @return {Boolean} false to tell the browser not to submit the form.
+ */
+shell.runStatement = function() {
+ var form = document.getElementById('form');
+
+ // build a XmlHttpRequest
+ var req = this.getXmlHttpRequest();
+ if (!req) {
+ document.getElementById('ajax-status').innerHTML =
+ "<span class='error'>Your browser doesn't support AJAX. :(</span>";
+ return false;
+ }
+
+ req.onreadystatechange = function() { shell.done(req); };
+
+ // build the query parameter string
+ var params = '';
+ for (i = 0; i < form.elements.length; i++) {
+ var elem = form.elements[i];
+ if (elem.type != 'submit' && elem.type != 'button' && elem.id != 'caret') {
+ var value = escape(elem.value).replace(/\+/g, '%2B'); // escape ignores +
+ params += '&' + elem.name + '=' + value;
+ }
+ }
+
+ // send the request and tell the user.
+ document.getElementById('statement').className = 'prompt processing';
+ req.open(form.method, form.action + '?' + params, true);
+ req.setRequestHeader('Content-type',
+ 'application/x-www-form-urlencoded;charset=UTF-8');
+ req.send(null);
+
+ return false;
+};
diff --git a/scripts/app_engine_server/gae_shell/static/spinner.gif b/scripts/app_engine_server/gae_shell/static/spinner.gif
new file mode 100644
index 0000000..3e58d6e
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/static/spinner.gif
Binary files differ
diff --git a/scripts/app_engine_server/gae_shell/templates/shell.html b/scripts/app_engine_server/gae_shell/templates/shell.html
new file mode 100644
index 0000000..123b200
--- /dev/null
+++ b/scripts/app_engine_server/gae_shell/templates/shell.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title> Interactive Shell </title>
+<script type="text/javascript" src="/gae_shell/static/shell.js"></script>
+<style type="text/css">
+body {
+ font-family: monospace;
+ font-size: 10pt;
+}
+
+p {
+ margin: 0.5em;
+}
+
+.prompt, #output {
+ width: 45em;
+ border: 1px solid silver;
+ background-color: #f5f5f5;
+ font-size: 10pt;
+ margin: 0.5em;
+ padding: 0.5em;
+ padding-right: 0em;
+ overflow-x: hidden;
+}
+
+#toolbar {
+ margin-left: 0.5em;
+ padding-left: 0.5em;
+}
+
+#caret {
+ width: 2.5em;
+ margin-right: 0px;
+ padding-right: 0px;
+ border-right: 0px;
+}
+
+#statement {
+ width: 43em;
+ margin-left: -1em;
+ padding-left: 0px;
+ border-left: 0px;
+ background-position: top right;
+ background-repeat: no-repeat;
+}
+
+.processing {
+ background-image: url("/gae_shell/static/spinner.gif");
+}
+
+#ajax-status {
+ font-weight: bold;
+}
+
+.message {
+ color: #8AD;
+ font-weight: bold;
+ font-style: italic;
+}
+
+.error {
+ color: #F44;
+}
+
+.username {
+ font-weight: bold;
+}
+
+</style>
+</head>
+
+<body>
+
+<p> Interactive server-side Python shell for
+<a href="http://code.google.com/appengine/">Google App Engine</a>.
+(<a href="http://code.google.com/p/google-app-engine-samples/">source</a>)
+</p>
+
+<textarea id="output" rows="22" readonly="readonly">
+{{ server_software }}
+Python {{ python_version }}
+</textarea>
+
+<form id="form" action="shell.do" method="get">
+ <nobr>
+ <textarea class="prompt" id="caret" readonly="readonly" rows="4"
+ onfocus="document.getElementById('statement').focus()"
+ >&gt;&gt;&gt;</textarea>
+ <textarea class="prompt" name="statement" id="statement" rows="4"
+ onkeypress="return shell.onPromptKeyPress(event);"></textarea>
+ </nobr>
+ <input type="hidden" name="session" value="{{ session }}" />
+ <input type="submit" style="display: none" />
+</form>
+
+<p id="ajax-status"></p>
+
+<p id="toolbar">
+{% if user %}
+ <span class="username">{{ user.nickname }}</span>
+ (<a href="{{ logout_url }}">log out</a>)
+{% else %}
+ <a href="{{ login_url }}">log in</a>
+{% endif %}
+ | Ctrl-Up/Down for history |
+<select id="submit_key">
+ <option value="enter">Enter</option>
+ <option value="ctrl-enter" selected="selected">Ctrl-Enter</option>
+</select>
+<label for="submit_key">submits</label>
+</p>
+
+<script type="text/javascript">
+document.getElementById('statement').focus();
+</script>
+
+</body>
+</html>
+
diff --git a/scripts/app_engine_server/index.yaml b/scripts/app_engine_server/index.yaml
new file mode 100644
index 0000000..8e6046d
--- /dev/null
+++ b/scripts/app_engine_server/index.yaml
@@ -0,0 +1,12 @@
+indexes:
+
+# AUTOGENERATED
+
+# This index.yaml is automatically updated whenever the dev_appserver
+# detects that a new type of query is run. If you want to manage the
+# index.yaml file manually, remove the above marker line (the line
+# saying "# AUTOGENERATED"). If you want to manage some indexes
+# manually, move them above the marker line. The index.yaml file is
+# automatically uploaded to the admin console when you next deploy
+# your application using appcfg.py.
+
diff --git a/scripts/app_engine_server/memcache_zipserve.py b/scripts/app_engine_server/memcache_zipserve.py
new file mode 100644
index 0000000..e11cfc5
--- /dev/null
+++ b/scripts/app_engine_server/memcache_zipserve.py
@@ -0,0 +1,412 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 Google Inc.
+#
+# 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.
+#
+
+"""A class to serve pages from zip files and use memcache for performance.
+
+This contains a class and a function to create an anonymous instance of the
+class to serve HTTP GET requests. Memcache is used to increase response speed
+and lower processing cycles used in serving. Credit to Guido van Rossum and
+his implementation of zipserve which served as a reference as I wrote this.
+
+ MemcachedZipHandler: Class that serves request
+ create_handler: method to create instance of MemcachedZipHandler
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+import email.Utils
+import logging
+import mimetypes
+import time
+import zipfile
+
+from google.appengine.api import memcache
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import util
+
+
+def create_handler(zip_files, max_age=None, public=None):
+ """Factory method to create a MemcachedZipHandler instance.
+
+ Args:
+ zip_files: A list of file names, or a list of lists of file name, first
+ member of file mappings. See MemcachedZipHandler documentation for
+ more information about using the list of lists format
+ max_age: The maximum client-side cache lifetime
+ public: Whether this should be declared public in the client-side cache
+ Returns:
+ A MemcachedZipHandler wrapped in a pretty, anonymous bow for use with App
+ Engine
+
+ Raises:
+ ValueError: if the zip_files argument is not a list
+ """
+ # verify argument integrity. If the argument is passed in list format,
+ # convert it to list of lists format
+
+ if zip_files and type(zip_files).__name__ == 'list':
+ num_items = len(zip_files)
+ while num_items > 0:
+ if type(zip_files[num_items - 1]).__name__ != 'list':
+ zip_files[num_items - 1] = [zip_files[num_items-1]]
+ num_items -= 1
+ else:
+ raise ValueError('File name arguments must be a list')
+
+ class HandlerWrapper(MemcachedZipHandler):
+ """Simple wrapper for an instance of MemcachedZipHandler.
+
+ I'm still not sure why this is needed
+ """
+
+ def get(self, name):
+ self.zipfilenames = zip_files
+ self.TrueGet(name)
+ if max_age is not None:
+ MAX_AGE = max_age
+ if public is not None:
+ PUBLIC = public
+
+ return HandlerWrapper
+
+
+class MemcachedZipHandler(webapp.RequestHandler):
+ """Handles get requests for a given URL.
+
+ Serves a GET request from a series of zip files. As files are served they are
+ put into memcache, which is much faster than retreiving them from the zip
+ source file again. It also uses considerably fewer CPU cycles.
+ """
+ zipfile_cache = {} # class cache of source zip files
+ MAX_AGE = 600 # max client-side cache lifetime
+ PUBLIC = True # public cache setting
+ CACHE_PREFIX = 'cache://' # memcache key prefix for actual URLs
+ NEG_CACHE_PREFIX = 'noncache://' # memcache key prefix for non-existant URL
+
+ def TrueGet(self, name):
+ """The top-level entry point to serving requests.
+
+ Called 'True' get because it does the work when called from the wrapper
+ class' get method
+
+ Args:
+ name: URL requested
+
+ Returns:
+ None
+ """
+ name = self.PreprocessUrl(name)
+
+ # see if we have the page in the memcache
+ resp_data = self.GetFromCache(name)
+ if resp_data is None:
+ logging.info('Cache miss for %s', name)
+ resp_data = self.GetFromNegativeCache(name)
+ if resp_data is None:
+ resp_data = self.GetFromStore(name)
+
+ # IF we have the file, put it in the memcache
+ # ELSE put it in the negative cache
+ if resp_data is not None:
+ self.StoreOrUpdateInCache(name, resp_data)
+ else:
+ logging.info('Adding %s to negative cache, serving 404', name)
+ self.StoreInNegativeCache(name)
+ self.Write404Error()
+ return
+ else:
+ self.Write404Error()
+ return
+
+ content_type, encoding = mimetypes.guess_type(name)
+ if content_type:
+ self.response.headers['Content-Type'] = content_type
+ self.SetCachingHeaders()
+ self.response.out.write(resp_data)
+
+ def PreprocessUrl(self, name):
+ """Any preprocessing work on the URL when it comes it.
+
+ Put any work related to interpretting the incoming URL here. For example,
+ this is used to redirect requests for a directory to the index.html file
+ in that directory. Subclasses should override this method to do different
+ preprocessing.
+
+ Args:
+ name: The incoming URL
+
+ Returns:
+ The processed URL
+ """
+ # handle special case of requesting the domain itself
+ if not name:
+ name = 'index.html'
+
+ # determine if this is a request for a directory
+ final_path_segment = name
+ final_slash_offset = name.rfind('/')
+ if final_slash_offset != len(name) - 1:
+ final_path_segment = name[final_slash_offset + 1:]
+ if final_path_segment.find('.') == -1:
+ name = ''.join([name, '/'])
+
+ # if this is a directory, redirect to index.html
+ if name[len(name) - 1:] == '/':
+ return '%s%s' % (name, 'index.html')
+ else:
+ return name
+
+ def GetFromStore(self, file_path):
+ """Retrieve file from zip files.
+
+ Get the file from the source, it must not have been in the memcache. If
+ possible, we'll use the zip file index to quickly locate where the file
+ should be found. (See MapToFileArchive documentation for assumptions about
+ file ordering.) If we don't have an index or don't find the file where the
+ index says we should, look through all the zip files to find it.
+
+ Args:
+ file_path: the file that we're looking for
+
+ Returns:
+ The contents of the requested file
+ """
+ resp_data = None
+ file_itr = iter(self.zipfilenames)
+
+ # check the index, if we have one, to see what archive the file is in
+ archive_name = self.MapFileToArchive(file_path)
+ if not archive_name:
+ archive_name = file_itr.next()[0]
+
+ while resp_data is None and archive_name:
+ zip_archive = self.LoadZipFile(archive_name)
+ if zip_archive:
+
+ # we expect some lookups will fail, and that's okay, 404s will deal
+ # with that
+ try:
+ resp_data = zip_archive.read(file_path)
+ except (KeyError, RuntimeError), err:
+ # no op
+ x = False
+ if resp_data is not None:
+ logging.info('%s read from %s', file_path, archive_name)
+
+ try:
+ archive_name = file_itr.next()[0]
+ except (StopIteration), err:
+ archive_name = False
+
+ return resp_data
+
+ def LoadZipFile(self, zipfilename):
+ """Convenience method to load zip file.
+
+ Just a convenience method to load the zip file from the data store. This is
+ useful if we ever want to change data stores and also as a means of
+ dependency injection for testing. This method will look at our file cache
+ first, and then load and cache the file if there's a cache miss
+
+ Args:
+ zipfilename: the name of the zip file to load
+
+ Returns:
+ The zip file requested, or None if there is an I/O error
+ """
+ zip_archive = None
+ zip_archive = self.zipfile_cache.get(zipfilename)
+ if zip_archive is None:
+ try:
+ zip_archive = zipfile.ZipFile(zipfilename)
+ self.zipfile_cache[zipfilename] = zip_archive
+ except (IOError, RuntimeError), err:
+ logging.error('Can\'t open zipfile %s, cause: %s' % (zipfilename,
+ err))
+ return zip_archive
+
+ def MapFileToArchive(self, file_path):
+ """Given a file name, determine what archive it should be in.
+
+ This method makes two critical assumptions.
+ (1) The zip files passed as an argument to the handler, if concatenated
+ in that same order, would result in a total ordering
+ of all the files. See (2) for ordering type.
+ (2) Upper case letters before lower case letters. The traversal of a
+ directory tree is depth first. A parent directory's files are added
+ before the files of any child directories
+
+ Args:
+ file_path: the file to be mapped to an archive
+
+ Returns:
+ The name of the archive where we expect the file to be
+ """
+ num_archives = len(self.zipfilenames)
+ while num_archives > 0:
+ target = self.zipfilenames[num_archives - 1]
+ if len(target) > 1:
+ if self.CompareFilenames(target[1], file_path) >= 0:
+ return target[0]
+ num_archives -= 1
+
+ return None
+
+ def CompareFilenames(self, file1, file2):
+ """Determines whether file1 is lexigraphically 'before' file2.
+
+ WARNING: This method assumes that paths are output in a depth-first,
+ with parent directories' files stored before childs'
+
+ We say that file1 is lexigraphically before file2 if the last non-matching
+ path segment of file1 is alphabetically before file2.
+
+ Args:
+ file1: the first file path
+ file2: the second file path
+
+ Returns:
+ A positive number if file1 is before file2
+ A negative number if file2 is before file1
+ 0 if filenames are the same
+ """
+ f1_segments = file1.split('/')
+ f2_segments = file2.split('/')
+
+ segment_ptr = 0
+ while (segment_ptr < len(f1_segments) and
+ segment_ptr < len(f2_segments) and
+ f1_segments[segment_ptr] == f2_segments[segment_ptr]):
+ segment_ptr += 1
+
+ if len(f1_segments) == len(f2_segments):
+
+ # we fell off the end, the paths much be the same
+ if segment_ptr == len(f1_segments):
+ return 0
+
+ # we didn't fall of the end, compare the segments where they differ
+ if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
+ return 1
+ elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
+ return -1
+ else:
+ return 0
+
+ # the number of segments differs, we either mismatched comparing
+ # directories, or comparing a file to a directory
+ else:
+
+ # IF we were looking at the last segment of one of the paths,
+ # the one with fewer segments is first because files come before
+ # directories
+ # ELSE we just need to compare directory names
+ if (segment_ptr + 1 == len(f1_segments) or
+ segment_ptr + 1 == len(f2_segments)):
+ return len(f2_segments) - len(f1_segments)
+ else:
+ if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
+ return 1
+ elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
+ return -1
+ else:
+ return 0
+
+ def SetCachingHeaders(self):
+ """Set caching headers for the request."""
+ max_age = self.MAX_AGE
+ self.response.headers['Expires'] = email.Utils.formatdate(
+ time.time() + max_age, usegmt=True)
+ cache_control = []
+ if self.PUBLIC:
+ cache_control.append('public')
+ cache_control.append('max-age=%d' % max_age)
+ self.response.headers['Cache-Control'] = ', '.join(cache_control)
+
+ def GetFromCache(self, filename):
+ """Get file from memcache, if available.
+
+ Args:
+ filename: The URL of the file to return
+
+ Returns:
+ The content of the file
+ """
+ return memcache.get('%s%s' % (self.CACHE_PREFIX, filename))
+
+ def StoreOrUpdateInCache(self, filename, data):
+ """Store data in the cache.
+
+ Store a piece of data in the memcache. Memcache has a maximum item size of
+ 1*10^6 bytes. If the data is too large, fail, but log the failure. Future
+ work will consider compressing the data before storing or chunking it
+
+ Args:
+ filename: the name of the file to store
+ data: the data of the file
+
+ Returns:
+ None
+ """
+ try:
+ if not memcache.add('%s%s' % (self.CACHE_PREFIX, filename), data):
+ memcache.replace('%s%s' % (self.CACHE_PREFIX, filename), data)
+ except (ValueError), err:
+ logging.warning('Data size too large to cache\n%s' % err)
+
+ def Write404Error(self):
+ """Ouptut a simple 404 response."""
+ self.error(404)
+ self.response.out.write(
+ ''.join(['<html><head><title>404: Not Found</title></head>',
+ '<body><b><h2>Error 404</h2><br/>',
+ 'File not found</b></body></html>']))
+
+ def StoreInNegativeCache(self, filename):
+ """If a non-existant URL is accessed, cache this result as well.
+
+ Future work should consider setting a maximum negative cache size to
+ prevent it from from negatively impacting the real cache.
+
+ Args:
+ filename: URL to add ot negative cache
+
+ Returns:
+ None
+ """
+ memcache.add('%s%s' % (self.NEG_CACHE_PREFIX, filename), -1)
+
+ def GetFromNegativeCache(self, filename):
+ """Retrieve from negative cache.
+
+ Args:
+ filename: URL to retreive
+
+ Returns:
+ The file contents if present in the negative cache.
+ """
+ return memcache.get('%s%s' % (self.NEG_CACHE_PREFIX, filename))
+
+
+def main():
+ application = webapp.WSGIApplication([('/([^/]+)/(.*)',
+ MemcachedZipHandler)])
+ util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/combine_sdks.sh b/scripts/combine_sdks.sh
index 89a1141..ebaa1c6 100755
--- a/scripts/combine_sdks.sh
+++ b/scripts/combine_sdks.sh
@@ -2,22 +2,36 @@
function replace()
{
- echo replacing $1
- rm -rf $UNZIPPED_BASE_DIR/$1
- cp -rf $UNZIPPED_IMAGE_DIR/$1 $UNZIPPED_BASE_DIR/$1
+ echo replacing $1
+ rm $V -rf "$UNZIPPED_BASE_DIR"/$1
+ cp $V -rf "$UNZIPPED_IMAGE_DIR"/$1 "$UNZIPPED_BASE_DIR"/$1
}
-BASE=$1
-IMAGES=$2
-OUTPUT=$3
+V=""
+Q="-q"
+if [ "$1" == "-v" ]; then
+ V="-v"
+ Q=""
+ shift
+fi
+
+NOZIP=""
+if [ "$1" == "-nozip" ]; then
+ NOZIP="1"
+ shift
+fi
-if [[ -z $BASE || -z $IMAGES || -z $OUTPUT ]] ; then
- echo "usage: combine_sdks.sh BASE IMAGES OUTPUT"
+BASE="$1"
+IMAGES="$2"
+OUTPUT="$3"
+
+if [[ -z "$BASE" || -z "$IMAGES" || -z "$OUTPUT" ]] ; then
+ echo "usage: combine_sdks.sh [-v] [-nozip] BASE IMAGES OUTPUT"
echo
echo " BASE and IMAGES should be sdk zip files. The system image files,"
echo " emulator and other runtime files will be copied from IMAGES and"
echo " everything else will be copied from BASE. All of this will be"
- echo " bundled into OUTPUT and zipped up again."
+ echo " bundled into OUTPUT and zipped up again (unless -nozip is specified)."
echo
exit 1
fi
@@ -26,15 +40,25 @@ TMP=$(mktemp -d)
TMP_ZIP=tmp.zip
-BASE_DIR=$TMP/base
-IMAGES_DIR=$TMP/images
-OUTPUT_TMP_ZIP=$BASE_DIR/$TMP_ZIP
+# determine executable extension
+case `uname -s` in
+ *_NT-*) # for Windows
+ EXE=.exe
+ ;;
+ *)
+ EXE=
+ ;;
+esac
+
+BASE_DIR="$TMP"/base
+IMAGES_DIR="$TMP"/images
+OUTPUT_TMP_ZIP="$BASE_DIR/$TMP_ZIP"
-unzip -q $BASE -d $BASE_DIR
-unzip -q $IMAGES -d $IMAGES_DIR
+unzip $Q "$BASE" -d "$BASE_DIR"
+unzip $Q "$IMAGES" -d "$IMAGES_DIR"
-UNZIPPED_BASE_DIR=$(echo $BASE_DIR/*)
-UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*)
+UNZIPPED_BASE_DIR=$(echo "$BASE_DIR"/*)
+UNZIPPED_IMAGE_DIR=$(echo "$IMAGES_DIR"/*)
#
# The commands to copy over the files that we want
@@ -42,21 +66,40 @@ UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*)
# replace tools/emulator # at this time we do not want the exe from SDK1.x
replace tools/lib/images
+replace tools/lib/res
+replace tools/lib/fonts
+replace tools/lib/layoutlib.jar
replace docs
replace android.jar
+for i in widgets categories broadcast_actions service_actions; do
+ replace tools/lib/$i.txt
+done
+
+if [ -d "$UNZIPPED_BASE_DIR"/usb_driver ]; then
+ replace usb_driver
+fi
+
#
# end
#
-pushd $BASE_DIR &> /dev/null
- # rename the directory to the leaf minus the .zip of OUTPUT
- LEAF=$(echo $OUTPUT | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
- mv * $LEAF
- # zip it
- zip -qr $TMP_ZIP $LEAF
-popd &> /dev/null
+if [ -z "$NOZIP" ]; then
+ pushd "$BASE_DIR" &> /dev/null
+ # rename the directory to the leaf minus the .zip of OUTPUT
+ LEAF=$(echo "$OUTPUT" | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
+ mv * "$LEAF"
+ # zip it
+ zip $V -qr "$TMP_ZIP" "$LEAF"
+ popd &> /dev/null
+
+ cp $V "$OUTPUT_TMP_ZIP" "$OUTPUT"
+ echo "Combined SDK available at $OUTPUT"
+else
+ OUT_DIR="${OUTPUT//.zip/}"
+ mv $V "$BASE_DIR"/* "$OUT_DIR"
+ echo "Unzipped combined SDK available at $OUT_DIR"
+fi
-cp $OUTPUT_TMP_ZIP $OUTPUT
+rm $V -rf "$TMP"
-rm -rf $TMP
diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 2db668e..f1531e0 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -31,52 +31,67 @@ import java.util.Map.Entry;
* <li>define flags for your actions.
* </ul>
* <p/>
- * To use, call {@link #parseArgs(String[])} and then call {@link #getValue(String, String)}.
+ * To use, call {@link #parseArgs(String[])} and then
+ * call {@link #getValue(String, String, String)}.
*/
public class CommandLineProcessor {
-
- /** Internal action name for all global flags. */
- public final static String GLOBAL_FLAG = "global";
- /** Internal action name for internally hidden flags.
- * This is currently used to store the requested action name. */
- public final static String INTERNAL_FLAG = "internal";
+ /** Internal verb name for internally hidden flags. */
+ public final static String GLOBAL_FLAG_VERB = "@@internal@@";
+
+ /** String to use when the verb doesn't need any object. */
+ public final static String NO_VERB_OBJECT = "";
+
/** The global help flag. */
public static final String KEY_HELP = "help";
/** The global verbose flag. */
public static final String KEY_VERBOSE = "verbose";
/** The global silent flag. */
public static final String KEY_SILENT = "silent";
- /** The internal action flag. */
- public static final String KEY_ACTION = "action";
+
+ /** Verb requested by the user. Null if none specified, which will be an error. */
+ private String mVerbRequested;
+ /** Direct object requested by the user. Can be null. */
+ private String mDirectObjectRequested;
- /** List of available actions.
+ /**
+ * Action definitions.
* <p/>
- * Each entry must be a 2-string array with first the action name and then
- * a description.
+ * Each entry is a string array with:
+ * <ul>
+ * <li> the verb.
+ * <li> a direct object (use #NO_VERB_OBJECT if there's no object).
+ * <li> a description.
+ * <li> an alternate form for the object (e.g. plural).
+ * </ul>
*/
private final String[][] mActions;
- /** The hash of all defined arguments.
+
+ private static final int ACTION_VERB_INDEX = 0;
+ private static final int ACTION_OBJECT_INDEX = 1;
+ private static final int ACTION_DESC_INDEX = 2;
+ private static final int ACTION_ALT_OBJECT_INDEX = 3;
+
+ /**
+ * The map of all defined arguments.
* <p/>
- * The key is a string "action/longName".
+ * The key is a string "verb/directObject/longName".
*/
private final HashMap<String, Arg> mArguments = new HashMap<String, Arg>();
+ /** Logger */
private final ISdkLog mLog;
public CommandLineProcessor(ISdkLog logger, String[][] actions) {
mLog = logger;
mActions = actions;
- define(MODE.STRING, false, INTERNAL_FLAG, null, KEY_ACTION,
- "Selected Action", null);
-
- define(MODE.BOOLEAN, false, GLOBAL_FLAG, "v", KEY_VERBOSE,
+ define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "v", KEY_VERBOSE,
"Verbose mode: errors, warnings and informational messages are printed.",
false);
- define(MODE.BOOLEAN, false, GLOBAL_FLAG, "s", KEY_SILENT,
+ define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "s", KEY_SILENT,
"Silent mode: only errors are printed out.",
false);
- define(MODE.BOOLEAN, false, GLOBAL_FLAG, "h", KEY_HELP,
+ define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "h", KEY_HELP,
"This help.",
false);
}
@@ -86,47 +101,54 @@ public class CommandLineProcessor {
/** Helper that returns true if --verbose was requested. */
public boolean isVerbose() {
- return ((Boolean) getValue(GLOBAL_FLAG, KEY_VERBOSE)).booleanValue();
+ return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_VERBOSE)).booleanValue();
}
/** Helper that returns true if --silent was requested. */
public boolean isSilent() {
- return ((Boolean) getValue(GLOBAL_FLAG, KEY_SILENT)).booleanValue();
+ return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_SILENT)).booleanValue();
}
/** Helper that returns true if --help was requested. */
public boolean isHelpRequested() {
- return ((Boolean) getValue(GLOBAL_FLAG, KEY_HELP)).booleanValue();
+ return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_HELP)).booleanValue();
+ }
+
+ /** Returns the verb name from the command-line. Can be null. */
+ public String getVerb() {
+ return mVerbRequested;
}
- /** Helper that returns the requested action name. */
- public String getActionRequested() {
- return (String) getValue(INTERNAL_FLAG, KEY_ACTION);
+ /** Returns the direct object name from the command-line. Can be null. */
+ public String getDirectObject() {
+ return mDirectObjectRequested;
}
//------------------
/**
* Raw access to parsed parameter values.
- * @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
+ * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}.
+ * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}.
* @param longFlagName The long flag name for the given action.
* @return The current value object stored in the parameter, which depends on the argument mode.
*/
- public Object getValue(String action, String longFlagName) {
- String key = action + "/" + longFlagName;
+ public Object getValue(String verb, String directObject, String longFlagName) {
+ String key = verb + "/" + directObject + "/" + longFlagName;
Arg arg = mArguments.get(key);
return arg.getCurrentValue();
}
/**
* Internal setter for raw parameter value.
- * @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
+ * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}.
+ * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}.
* @param longFlagName The long flag name for the given action.
* @param value The new current value object stored in the parameter, which depends on the
* argument mode.
*/
- protected void setValue(String action, String longFlagName, Object value) {
- String key = action + "/" + longFlagName;
+ protected void setValue(String verb, String directObject, String longFlagName, Object value) {
+ String key = verb + "/" + directObject + "/" + longFlagName;
Arg arg = mArguments.get(key);
arg.setCurrentValue(value);
}
@@ -140,114 +162,172 @@ public class CommandLineProcessor {
*/
public void parseArgs(String[] args) {
String needsHelp = null;
- String action = null;
-
- int n = args.length;
- for (int i = 0; i < n; i++) {
- Arg arg = null;
- String a = args[i];
- if (a.startsWith("--")) {
- arg = findLongArg(action, a.substring(2));
- } else if (a.startsWith("-")) {
- arg = findShortArg(action, a.substring(1));
- }
-
- // Not a keyword and we don't have an action yet, this should be an action
- if (arg == null && action == null) {
-
- if (a.startsWith("-")) {
- // Got a keyword but not valid for global flags
- needsHelp = String.format(
- "Flag '%1$s' is not a valid global flag. Did you mean to specify it after the action name?",
- a, action);
- break;
- }
-
- for (String[] actionDesc : mActions) {
- if (actionDesc[0].equals(a)) {
- action = a;
- break;
- }
- }
-
- if (action == null) {
- needsHelp = String.format(
- "Expected action name after global parameters but found %1$s instead.",
- a);
- break;
+ String verb = null;
+ String directObject = null;
+
+ try {
+ int n = args.length;
+ for (int i = 0; i < n; i++) {
+ Arg arg = null;
+ String a = args[i];
+ if (a.startsWith("--")) {
+ arg = findLongArg(verb, directObject, a.substring(2));
+ } else if (a.startsWith("-")) {
+ arg = findShortArg(verb, directObject, a.substring(1));
}
- } else if (arg == null && action != null) {
- // Got a keyword but not valid for the current action
- needsHelp = String.format(
- "Flag '%1$s' is not valid for action '%2$s'.",
- a, action);
- break;
- } else if (arg != null) {
- // Process keyword
- String error = null;
- if (arg.getMode().needsExtra()) {
- if (++i >= n) {
- needsHelp = String.format("Missing argument for flag %1$s.", a);
- break;
+ // No matching argument name found
+ if (arg == null) {
+ // Does it looks like a dashed parameter?
+ if (a.startsWith("-")) {
+ if (verb == null || directObject == null) {
+ // It looks like a dashed parameter and we don't have a a verb/object
+ // set yet, the parameter was just given too early.
+
+ needsHelp = String.format(
+ "Flag '%1$s' is not a valid global flag. Did you mean to specify it after the verb/object name?",
+ a);
+ return;
+ } else {
+ // It looks like a dashed parameter and but it is unknown by this
+ // verb-object combination
+
+ needsHelp = String.format(
+ "Flag '%1$s' is not valid for '%2$s %3$s'.",
+ a, verb, directObject);
+ return;
+ }
}
- error = arg.getMode().process(arg, args[i]);
- } else {
- error = arg.getMode().process(arg, null);
-
- // If we just toggled help, we want to exit now without printing any error.
- // We do this test here only when a Boolean flag is toggled since booleans
- // are the only flags that don't take parameters and help is a boolean.
- if (isHelpRequested()) {
- printHelpAndExit(null);
- // The call above should terminate however in unit tests we override
- // it so we still need to return here.
+ if (verb == null) {
+ // Fill verb first. Find it.
+ for (String[] actionDesc : mActions) {
+ if (actionDesc[ACTION_VERB_INDEX].equals(a)) {
+ verb = a;
+ break;
+ }
+ }
+
+ // Error if it was not a valid verb
+ if (verb == null) {
+ needsHelp = String.format(
+ "Expected verb after global parameters but found '%1$s' instead.",
+ a);
+ return;
+ }
+
+ } else if (directObject == null) {
+ // Then fill the direct object. Find it.
+ for (String[] actionDesc : mActions) {
+ if (actionDesc[ACTION_VERB_INDEX].equals(verb)) {
+ if (actionDesc[ACTION_OBJECT_INDEX].equals(a)) {
+ directObject = a;
+ break;
+ } else if (actionDesc.length > ACTION_ALT_OBJECT_INDEX &&
+ actionDesc[ACTION_ALT_OBJECT_INDEX].equals(a)) {
+ // if the alternate form exist and is used, we internally
+ // only memorize the default direct object form.
+ directObject = actionDesc[ACTION_OBJECT_INDEX];
+ break;
+ }
+ }
+ }
+
+ // Error if it was not a valid object for that verb
+ if (directObject == null) {
+ needsHelp = String.format(
+ "Expected verb after global parameters but found '%1$s' instead.",
+ a);
+ return;
+
+ }
+ }
+ } else if (arg != null) {
+ // Process keyword
+ String error = null;
+ if (arg.getMode().needsExtra()) {
+ if (++i >= n) {
+ needsHelp = String.format("Missing argument for flag %1$s.", a);
+ return;
+ }
+
+ error = arg.getMode().process(arg, args[i]);
+ } else {
+ error = arg.getMode().process(arg, null);
+
+ // If we just toggled help, we want to exit now without printing any error.
+ // We do this test here only when a Boolean flag is toggled since booleans
+ // are the only flags that don't take parameters and help is a boolean.
+ if (isHelpRequested()) {
+ printHelpAndExit(null);
+ // The call above should terminate however in unit tests we override
+ // it so we still need to return here.
+ return;
+ }
+ }
+
+ if (error != null) {
+ needsHelp = String.format("Invalid usage for flag %1$s: %2$s.", a, error);
return;
}
}
-
- if (error != null) {
- needsHelp = String.format("Invalid usage for flag %1$s: %2$s.", a, error);
- break;
- }
}
- }
- if (needsHelp == null) {
- if (action == null) {
- needsHelp = "Missing action name.";
- } else {
- // Validate that all mandatory arguments are non-null for this action
- String missing = null;
- boolean plural = false;
- for (Entry<String, Arg> entry : mArguments.entrySet()) {
- Arg arg = entry.getValue();
- if (arg.getAction().equals(action)) {
- if (arg.isMandatory() && arg.getCurrentValue() == null) {
- if (missing == null) {
- missing = "--" + arg.getLongArg();
- } else {
- missing += ", --" + arg.getLongArg();
- plural = true;
+ if (needsHelp == null) {
+ if (verb == null) {
+ needsHelp = "Missing verb name.";
+ } else {
+ if (directObject == null) {
+ // Make sure this verb has an optional direct object
+ for (String[] actionDesc : mActions) {
+ if (actionDesc[ACTION_VERB_INDEX].equals(verb) &&
+ actionDesc[ACTION_OBJECT_INDEX].equals(NO_VERB_OBJECT)) {
+ directObject = NO_VERB_OBJECT;
+ break;
}
}
+
+ if (directObject == null) {
+ needsHelp = String.format("Missing object name for verb '%1$s'.", verb);
+ return;
+ }
+ }
+
+ // Validate that all mandatory arguments are non-null for this action
+ String missing = null;
+ boolean plural = false;
+ for (Entry<String, Arg> entry : mArguments.entrySet()) {
+ Arg arg = entry.getValue();
+ if (arg.getVerb().equals(verb) &&
+ arg.getDirectObject().equals(directObject)) {
+ if (arg.isMandatory() && arg.getCurrentValue() == null) {
+ if (missing == null) {
+ missing = "--" + arg.getLongArg();
+ } else {
+ missing += ", --" + arg.getLongArg();
+ plural = true;
+ }
+ }
+ }
+ }
+
+ if (missing != null) {
+ needsHelp = String.format(
+ "The %1$s %2$s must be defined for action '%3$s %4$s'",
+ plural ? "parameters" : "parameter",
+ missing,
+ verb,
+ directObject);
}
- }
- if (missing != null) {
- needsHelp = String.format("The %1$s %2$s must be defined for action '%3$s'",
- plural ? "parameters" : "parameter",
- missing,
- action);
+ mVerbRequested = verb;
+ mDirectObjectRequested = directObject;
}
-
- setValue(INTERNAL_FLAG, KEY_ACTION, action);
}
- }
-
- if (needsHelp != null) {
- printHelpAndExitForAction(action, needsHelp);
+ } finally {
+ if (needsHelp != null) {
+ printHelpAndExitForAction(verb, directObject, needsHelp);
+ }
}
}
@@ -255,11 +335,14 @@ public class CommandLineProcessor {
* Finds an {@link Arg} given an action name and a long flag name.
* @return The {@link Arg} found or null.
*/
- protected Arg findLongArg(String action, String longName) {
- if (action == null) {
- action = GLOBAL_FLAG;
+ protected Arg findLongArg(String verb, String directObject, String longName) {
+ if (verb == null) {
+ verb = GLOBAL_FLAG_VERB;
+ }
+ if (directObject == null) {
+ directObject = NO_VERB_OBJECT;
}
- String key = action + "/" + longName;
+ String key = verb + "/" + directObject + "/" + longName;
return mArguments.get(key);
}
@@ -267,14 +350,17 @@ public class CommandLineProcessor {
* Finds an {@link Arg} given an action name and a short flag name.
* @return The {@link Arg} found or null.
*/
- protected Arg findShortArg(String action, String shortName) {
- if (action == null) {
- action = GLOBAL_FLAG;
+ protected Arg findShortArg(String verb, String directObject, String shortName) {
+ if (verb == null) {
+ verb = GLOBAL_FLAG_VERB;
+ }
+ if (directObject == null) {
+ directObject = NO_VERB_OBJECT;
}
for (Entry<String, Arg> entry : mArguments.entrySet()) {
Arg arg = entry.getValue();
- if (arg.getAction().equals(action)) {
+ if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
if (shortName.equals(arg.getShortArg())) {
return arg;
}
@@ -291,18 +377,22 @@ public class CommandLineProcessor {
* @param args Arguments for String.format
*/
public void printHelpAndExit(String errorFormat, Object... args) {
- printHelpAndExitForAction(null /*actionFilter*/, errorFormat, args);
+ printHelpAndExitForAction(null /*verb*/, null /*directObject*/, errorFormat, args);
}
/**
* Prints the help/usage and exits.
*
- * @param actionFilter If null, displays help for all actions. If not null, display help only
- * for that specific action. In all cases also display general usage and action list.
+ * @param verb If null, displays help for all verbs. If not null, display help only
+ * for that specific verb. In all cases also displays general usage and action list.
+ * @param directObject If null, displays help for all verb objects.
+ * If not null, displays help only for that specific action
+ * In all cases also display general usage and action list.
* @param errorFormat Optional error message to print prior to usage using String.format
* @param args Arguments for String.format
*/
- public void printHelpAndExitForAction(String actionFilter, String errorFormat, Object... args) {
+ public void printHelpAndExitForAction(String verb, String directObject,
+ String errorFormat, Object... args) {
if (errorFormat != null) {
stderr(errorFormat, args);
}
@@ -316,25 +406,27 @@ public class CommandLineProcessor {
" android [global options] action [action options]\n" +
"\n" +
"Global options:");
- listOptions(GLOBAL_FLAG);
+ listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT);
- stdout("\nValid actions:");
+ stdout("\nValid actions are composed of a verb and an optional direct object:");
for (String[] action : mActions) {
- String filler = "";
- int len = action[0].length();
- if (len < 10) {
- filler = " ".substring(len);
- }
- stdout("- %1$s:%2$s %3$s", action[0], filler, action[1]);
+ stdout("- %1$6s %2$-7s: %3$s",
+ action[ACTION_VERB_INDEX],
+ action[ACTION_OBJECT_INDEX],
+ action[ACTION_DESC_INDEX]);
}
for (String[] action : mActions) {
- if (actionFilter == null || actionFilter.equals(action[0])) {
- stdout("\nAction \"%1$s\":", action[0]);
- stdout(" %1$s", action[1]);
- stdout("Options:");
- listOptions(action[0]);
+ if (verb == null || verb.equals(action[ACTION_VERB_INDEX])) {
+ if (directObject == null || directObject.equals(action[ACTION_OBJECT_INDEX])) {
+ stdout("\nAction \"%1$s %2$s\":",
+ action[ACTION_VERB_INDEX],
+ action[ACTION_OBJECT_INDEX]);
+ stdout(" %1$s", action[ACTION_DESC_INDEX]);
+ stdout("Options:");
+ listOptions(action[ACTION_VERB_INDEX], action[ACTION_OBJECT_INDEX]);
+ }
}
}
@@ -344,11 +436,11 @@ public class CommandLineProcessor {
/**
* Internal helper to print all the option flags for a given action name.
*/
- protected void listOptions(String action) {
+ protected void listOptions(String verb, String directObject) {
int numOptions = 0;
for (Entry<String, Arg> entry : mArguments.entrySet()) {
Arg arg = entry.getValue();
- if (arg.getAction().equals(action)) {
+ if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
String value = "";
if (arg.getDefaultValue() instanceof String[]) {
@@ -483,21 +575,22 @@ public class CommandLineProcessor {
* or a String array (in which case the first item is the current by default.)
*/
static class Arg {
- private final String mAction;
+ private final String mVerb;
+ private final String mDirectObject;
private final String mShortName;
private final String mLongName;
private final String mDescription;
private final Object mDefaultValue;
- private Object mCurrentValue;
private final MODE mMode;
private final boolean mMandatory;
+ private Object mCurrentValue;
/**
* Creates a new argument flag description.
*
* @param mode The {@link MODE} for the argument.
* @param mandatory True if this argument is mandatory for this action.
- * @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
+ * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null.
@@ -505,14 +598,16 @@ public class CommandLineProcessor {
*/
public Arg(MODE mode,
boolean mandatory,
- String action,
+ String verb,
+ String directObject,
String shortName,
String longName,
String description,
Object defaultValue) {
mMode = mode;
mMandatory = mandatory;
- mAction = action;
+ mVerb = verb;
+ mDirectObject = directObject;
mShortName = shortName;
mLongName = longName;
mDescription = description;
@@ -540,8 +635,12 @@ public class CommandLineProcessor {
return mDescription;
}
- public String getAction() {
- return mAction;
+ public String getVerb() {
+ return mVerb;
+ }
+
+ public String getDirectObject() {
+ return mDirectObject;
}
public Object getDefaultValue() {
@@ -565,7 +664,8 @@ public class CommandLineProcessor {
* Internal helper to define a new argument for a give action.
*
* @param mode The {@link MODE} for the argument.
- * @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
+ * @param verb The verb name. Can be #INTERNAL_VERB.
+ * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null.
@@ -573,14 +673,19 @@ public class CommandLineProcessor {
*/
protected void define(MODE mode,
boolean mandatory,
- String action,
+ String verb,
+ String directObject,
String shortName, String longName,
String description, Object defaultValue) {
assert(mandatory || mode == MODE.BOOLEAN); // a boolean mode cannot be mandatory
- String key = action + "/" + longName;
+ if (directObject == null) {
+ directObject = NO_VERB_OBJECT;
+ }
+
+ String key = verb + "/" + directObject + "/" + longName;
mArguments.put(key, new Arg(mode, mandatory,
- action, shortName, longName, description, defaultValue));
+ verb, directObject, shortName, longName, description, defaultValue));
}
/**
diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 1544f5b..7965e87 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -23,12 +23,12 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.avd.AvdManager;
+import com.android.sdklib.avd.HardwareProperties;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
+import com.android.sdklib.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.project.ProjectCreator;
import com.android.sdklib.project.ProjectCreator.OutputLevel;
-import com.android.sdklib.vm.HardwareProperties;
-import com.android.sdklib.vm.VmManager;
-import com.android.sdklib.vm.HardwareProperties.HardwareProperty;
-import com.android.sdklib.vm.VmManager.VmInfo;
import java.io.File;
import java.io.IOException;
@@ -56,8 +56,8 @@ class Main {
private ISdkLog mSdkLog;
/** The SDK manager parses the SDK folder and gives access to the content. */
private SdkManager mSdkManager;
- /** Virtual Machine manager to access the list of VMs or create new ones. */
- private VmManager mVmManager;
+ /** Virtual Machine manager to access the list of AVDs or create new ones. */
+ private AvdManager mAvdManager;
/** Command-line processor with options specific to SdkManager. */
private SdkCommandLine mSdkCommandLine;
/** The working directory, either null or set to an existing absolute canonical directory. */
@@ -183,26 +183,31 @@ class Main {
* Actually do an action...
*/
private void doAction() {
- String action = mSdkCommandLine.getActionRequested();
+ String verb = mSdkCommandLine.getVerb();
+ String directObject = mSdkCommandLine.getDirectObject();
- if (SdkCommandLine.ACTION_LIST.equals(action)) {
+ if (SdkCommandLine.VERB_LIST.equals(verb)) {
// list action.
- if (SdkCommandLine.ARG_TARGET.equals(mSdkCommandLine.getListFilter())) {
+ if (SdkCommandLine.OBJECT_TARGET.equals(directObject)) {
displayTargetList();
- } else if (SdkCommandLine.ARG_VM.equals(mSdkCommandLine.getListFilter())) {
- displayVmList();
+ } else if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
+ displayAvdList();
} else {
displayTargetList();
- displayVmList();
+ displayAvdList();
}
- } else if (SdkCommandLine.ACTION_NEW_VM.equals(action)) {
- createVm();
- } else if (SdkCommandLine.ACTION_NEW_PROJECT.equals(action)) {
+
+ } else if (SdkCommandLine.VERB_CREATE.equals(verb) &&
+ SdkCommandLine.OBJECT_AVD.equals(directObject)) {
+ createAvd();
+
+ } else if (SdkCommandLine.VERB_CREATE.equals(verb) &&
+ SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
// get the target and try to resolve it.
- int targetId = mSdkCommandLine.getNewProjectTargetId();
+ int targetId = mSdkCommandLine.getCreateProjectTargetId();
IAndroidTarget[] targets = mSdkManager.getTargets();
if (targetId < 1 || targetId > targets.length) {
- errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
+ errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
IAndroidTarget target = targets[targetId - 1];
@@ -213,22 +218,24 @@ class Main {
OutputLevel.NORMAL,
mSdkLog);
- String projectDir = getProjectLocation(mSdkCommandLine.getNewProjectLocation());
+ String projectDir = getProjectLocation(mSdkCommandLine.getCreateProjectLocation());
creator.createProject(projectDir,
- mSdkCommandLine.getNewProjectName(),
- mSdkCommandLine.getNewProjectPackage(),
- mSdkCommandLine.getNewProjectActivity(),
+ mSdkCommandLine.getCreateProjectName(),
+ mSdkCommandLine.getCreateProjectPackage(),
+ mSdkCommandLine.getCreateProjectActivity(),
target,
false /* isTestProject*/);
- } else if (SdkCommandLine.ACTION_UPDATE_PROJECT.equals(action)) {
+
+ } else if (SdkCommandLine.VERB_UPDATE.equals(verb) &&
+ SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
// get the target and try to resolve it.
IAndroidTarget target = null;
int targetId = mSdkCommandLine.getUpdateProjectTargetId();
if (targetId >= 0) {
IAndroidTarget[] targets = mSdkManager.getTargets();
if (targetId < 1 || targetId > targets.length) {
- errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
+ errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
target = targets[targetId - 1];
@@ -340,20 +347,20 @@ class Main {
}
/**
- * Displays the list of available VMs.
+ * Displays the list of available AVDs.
*/
- private void displayVmList() {
+ private void displayAvdList() {
try {
- mVmManager = new VmManager(mSdkManager, null /* sdklog */);
+ mAvdManager = new AvdManager(mSdkManager, null /* sdklog */);
- mSdkLog.printf("Available Android VMs:\n");
+ mSdkLog.printf("Available Android Virtual Devices:\n");
int index = 1;
- for (VmInfo info : mVmManager.getVms()) {
+ for (AvdInfo info : mAvdManager.getAvds()) {
mSdkLog.printf("[%d] %s\n", index, info.getName());
mSdkLog.printf(" Path: %s\n", info.getPath());
- // get the target of the Vm
+ // get the target of the AVD
IAndroidTarget target = info.getTarget();
if (target.isPlatform()) {
mSdkLog.printf(" Target: %s (API level %d)\n", target.getName(),
@@ -373,57 +380,85 @@ class Main {
}
/**
- * Creates a new VM. This is a text based creation with command line prompt.
+ * Creates a new AVD. This is a text based creation with command line prompt.
*/
- private void createVm() {
+ private void createAvd() {
// find a matching target
- int targetId = mSdkCommandLine.getNewVmTargetId();
+ int targetId = mSdkCommandLine.getCreateAvdTargetId();
IAndroidTarget target = null;
if (targetId >= 1 && targetId <= mSdkManager.getTargets().length) {
target = mSdkManager.getTargets()[targetId-1]; // target it is 1-based
} else {
- errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
+ errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
try {
- mVmManager = new VmManager(mSdkManager, mSdkLog);
+ boolean removePrevious = false;
+ mAvdManager = new AvdManager(mSdkManager, mSdkLog);
- String vmName = mSdkCommandLine.getNewVmName();
- VmInfo info = mVmManager.getVm(vmName);
+ String avdName = mSdkCommandLine.getCreateAvdName();
+ AvdInfo info = mAvdManager.getAvd(avdName);
if (info != null) {
- errorAndExit("VM %s already exists.", vmName);
- } else {
- String vmParentFolder = mSdkCommandLine.getNewVmLocation();
- if (vmParentFolder == null) {
- vmParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
+ if (mSdkCommandLine.getCreateAvdForce()) {
+ removePrevious = true;
+ mSdkLog.warning(
+ "Android Virtual Device '%s' already exists and will be replaced.",
+ avdName);
+ } else {
+ errorAndExit("Android Virtual Device '%s' already exists.", avdName);
+ return;
}
+ }
- Map<String, String> hardwareConfig = null;
- if (target.isPlatform()) {
- try {
- hardwareConfig = promptForHardware(target);
- } catch (IOException e) {
- errorAndExit(e.getMessage());
- }
+ String avdParentFolder = mSdkCommandLine.getCreateAvdLocation();
+ if (avdParentFolder == null) {
+ avdParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
+ }
+
+ Map<String, String> hardwareConfig = null;
+ if (target.isPlatform()) {
+ try {
+ hardwareConfig = promptForHardware(target);
+ } catch (IOException e) {
+ errorAndExit(e.getMessage());
}
+ }
+
+ AvdInfo oldAvdInfo = null;
+ if (removePrevious) {
+ oldAvdInfo = mAvdManager.getAvd(avdName);
+ }
- mVmManager.createVm(vmParentFolder,
- mSdkCommandLine.getNewVmName(),
- target,
- mSdkCommandLine.getNewVmSkin(),
- mSdkCommandLine.getNewVmSdCard(),
- hardwareConfig,
- mSdkLog);
+ AvdInfo newAvdInfo = mAvdManager.createAvd(avdParentFolder,
+ avdName,
+ target,
+ mSdkCommandLine.getCreateAvdSkin(),
+ mSdkCommandLine.getCreateAvdSdCard(),
+ hardwareConfig,
+ removePrevious,
+ mSdkLog);
+
+ if (newAvdInfo != null &&
+ oldAvdInfo != null &&
+ !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
+ mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
+ // Remove the old data directory
+ File dir = new File(oldAvdInfo.getPath());
+ mAvdManager.recursiveDelete(dir);
+ dir.delete();
+ // Remove old avd info from manager
+ mAvdManager.removeAvd(oldAvdInfo);
}
+
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
}
/**
- * Prompts the user to setup a hardware config for a Platform-based VM.
+ * Prompts the user to setup a hardware config for a Platform-based AVD.
* @throws IOException
*/
private Map<String, String> promptForHardware(IAndroidTarget createTarget) throws IOException {
diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 08626a8..fe93396 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -25,152 +25,218 @@ import com.android.sdklib.SdkManager;
*/
public class SdkCommandLine extends CommandLineProcessor {
- public static final String ARG_ALIAS = "alias";
+ public final static String VERB_LIST = "list";
+ public final static String VERB_CREATE = "create";
+ public final static String VERB_RENAME = "rename";
+ public final static String VERB_MOVE = "move";
+ public final static String VERB_DELETE = "delete";
+ public final static String VERB_UPDATE = "update";
+
+ public static final String OBJECT_AVD = "avd";
+ public static final String OBJECT_AVDS = "avds";
+ public static final String OBJECT_TARGET = "target";
+ public static final String OBJECT_TARGETS = "targets";
+ public static final String OBJECT_PROJECT = "project";
+
+ public static final String ARG_ALIAS = "alias";
public static final String ARG_ACTIVITY = "activity";
- public static final String ARG_VM = "vm";
- public static final String ARG_TARGET = "target";
- public static final String ARG_ALL = "all";
+
+ public static final String KEY_ACTIVITY = ARG_ACTIVITY;
+ public static final String KEY_PACKAGE = "package";
+ public static final String KEY_MODE = "mode";
+ public static final String KEY_TARGET_ID = OBJECT_TARGET;
+ public static final String KEY_NAME = "name";
+ public static final String KEY_PATH = "path";
+ public static final String KEY_FILTER = "filter";
+ public static final String KEY_SKIN = "skin";
+ public static final String KEY_SDCARD = "sdcard";
+ public static final String KEY_FORCE = "force";
+
+ /**
+ * Action definitions for SdkManager command line.
+ * <p/>
+ * Each entry is a string array with:
+ * <ul>
+ * <li> the verb.
+ * <li> an object (use #NO_VERB_OBJECT if there's no object).
+ * <li> a description.
+ * <li> an alternate form for the object (e.g. plural).
+ * </ul>
+ */
+ private final static String[][] ACTIONS = {
+ { VERB_LIST,
+ NO_VERB_OBJECT,
+ "Lists existing targets or virtual devices." },
+ { VERB_LIST,
+ OBJECT_AVD,
+ "Lists existing Android Virtual Devices.",
+ OBJECT_AVDS },
+ { VERB_LIST,
+ OBJECT_TARGET,
+ "Lists existing targets.",
+ OBJECT_TARGETS },
- public static final String KEY_ACTIVITY = ARG_ACTIVITY;
- public static final String KEY_PACKAGE = "package";
- public static final String KEY_MODE = "mode";
- public static final String KEY_TARGET_ID = ARG_TARGET;
- public static final String KEY_NAME = "name";
- public static final String KEY_OUT = "out";
- public static final String KEY_FILTER = "filter";
- public static final String KEY_SKIN = "skin";
- public static final String KEY_SDCARD = "sdcard";
-
- public final static String ACTION_LIST = "list";
- public final static String ACTION_NEW_VM = ARG_VM;
- public final static String ACTION_NEW_PROJECT = "project";
- public final static String ACTION_UPDATE_PROJECT = "update";
+ { VERB_CREATE,
+ OBJECT_AVD,
+ "Creates a new Android Virtual Device." },
+ { VERB_RENAME,
+ OBJECT_AVD,
+ "Renames a new Android Virtual Device." },
+ { VERB_MOVE,
+ OBJECT_AVD,
+ "Moves a new Android Virtual Device." },
+ { VERB_DELETE,
+ OBJECT_AVD,
+ "Deletes a new Android Virtual Device." },
- private final static String[][] ACTIONS = {
- { ACTION_LIST,
- "Lists existing targets or VMs." },
- { ACTION_NEW_VM,
- "Creates a new VM." },
- { ACTION_NEW_PROJECT,
- "Creates a new project using a template." },
- { ACTION_UPDATE_PROJECT,
- "Updates a project from existing source (must have an AndroidManifest.xml)." },
+ { VERB_CREATE,
+ OBJECT_PROJECT,
+ "Creates a new Android Project." },
+ { VERB_UPDATE,
+ OBJECT_PROJECT,
+ "Updates an Android Project (must have an AndroidManifest.xml)." },
};
public SdkCommandLine(ISdkLog logger) {
super(logger, ACTIONS);
- define(MODE.ENUM, false, ACTION_LIST, "f", KEY_FILTER,
- "List filter", new String[] { ARG_ALL, ARG_TARGET, ARG_VM });
-
- define(MODE.STRING, false, ACTION_NEW_VM, "o", KEY_OUT,
- "Location path of new VM", null);
- define(MODE.STRING, true, ACTION_NEW_VM, "n", KEY_NAME,
- "Name of the new VM", null);
- define(MODE.INTEGER, true, ACTION_NEW_VM, "t", KEY_TARGET_ID,
- "Target id of the new VM", null);
- define(MODE.STRING, true, ACTION_NEW_VM, "s", KEY_SKIN,
- "Skin of the new VM", null);
- define(MODE.STRING, false, ACTION_NEW_VM, "c", KEY_SDCARD,
- "Path to a shared SD card image, or size of a new sdcard for the new VM", null);
-
- define(MODE.ENUM, true, ACTION_NEW_PROJECT, "m", KEY_MODE,
+ define(MODE.STRING, false,
+ VERB_CREATE, OBJECT_AVD,
+ "p", KEY_PATH,
+ "Location path of the parent directory where the new AVD will be created", null);
+ define(MODE.STRING, true,
+ VERB_CREATE, OBJECT_AVD,
+ "n", KEY_NAME,
+ "Name of the new AVD", null);
+ define(MODE.INTEGER, true,
+ VERB_CREATE, OBJECT_AVD,
+ "t", KEY_TARGET_ID,
+ "Target id of the new AVD", null);
+ define(MODE.STRING, true,
+ VERB_CREATE, OBJECT_AVD,
+ "s", KEY_SKIN,
+ "Skin of the new AVD", null);
+ define(MODE.STRING, false,
+ VERB_CREATE, OBJECT_AVD,
+ "c", KEY_SDCARD,
+ "Path to a shared SD card image, or size of a new sdcard for the new AVD", null);
+ define(MODE.BOOLEAN, false,
+ VERB_CREATE, OBJECT_AVD,
+ "f", KEY_FORCE,
+ "Force creation (override an existing AVD)", false);
+
+ define(MODE.ENUM, true,
+ VERB_CREATE, OBJECT_PROJECT,
+ "m", KEY_MODE,
"Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
- define(MODE.STRING, true, ACTION_NEW_PROJECT, "o", KEY_OUT,
+ define(MODE.STRING, true,
+ VERB_CREATE, OBJECT_PROJECT,
+ "p", KEY_PATH,
"Location path of new project", null);
- define(MODE.INTEGER, true, ACTION_NEW_PROJECT, "t", KEY_TARGET_ID,
+ define(MODE.INTEGER, true,
+ VERB_CREATE, OBJECT_PROJECT,
+ "t", KEY_TARGET_ID,
"Target id of the new project", null);
- define(MODE.STRING, true, ACTION_NEW_PROJECT, "p", KEY_PACKAGE,
+ define(MODE.STRING, true,
+ VERB_CREATE, OBJECT_PROJECT,
+ "k", KEY_PACKAGE,
"Package name", null);
- define(MODE.STRING, true, ACTION_NEW_PROJECT, "a", KEY_ACTIVITY,
+ define(MODE.STRING, true,
+ VERB_CREATE, OBJECT_PROJECT,
+ "a", KEY_ACTIVITY,
"Activity name", null);
- define(MODE.STRING, false, ACTION_NEW_PROJECT, "n", KEY_NAME,
+ define(MODE.STRING, false,
+ VERB_CREATE, OBJECT_PROJECT,
+ "n", KEY_NAME,
"Project name", null);
- define(MODE.STRING, true, ACTION_UPDATE_PROJECT, "o", KEY_OUT,
+ define(MODE.STRING, true,
+ VERB_UPDATE, OBJECT_PROJECT,
+ "p", KEY_PATH,
"Location path of the project", null);
- define(MODE.INTEGER, true, ACTION_UPDATE_PROJECT, "t", KEY_TARGET_ID,
+ define(MODE.INTEGER, true,
+ VERB_UPDATE, OBJECT_PROJECT,
+ "t", KEY_TARGET_ID,
"Target id to set for the project", -1);
- define(MODE.STRING, false, ACTION_UPDATE_PROJECT, "n", KEY_NAME,
+ define(MODE.STRING, false,
+ VERB_UPDATE, OBJECT_PROJECT,
+ "n", KEY_NAME,
"Project name", null);
}
- // -- some helpers for list action flags
-
- /** Helper to retrieve the --filter for the list action. */
- public String getListFilter() {
- return ((String) getValue(ACTION_LIST, KEY_FILTER));
- }
-
- // -- some helpers for vm action flags
+ // -- some helpers for AVD action flags
- /** Helper to retrieve the --out location for the new vm action. */
- public String getNewVmLocation() {
- return ((String) getValue(ACTION_NEW_VM, KEY_OUT));
+ /** Helper to retrieve the --out location for the new AVD action. */
+ public String getCreateAvdLocation() {
+ return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_PATH));
}
- /** Helper to retrieve the --target id for the new vm action. */
- public int getNewVmTargetId() {
- return ((Integer) getValue(ACTION_NEW_VM, KEY_TARGET_ID)).intValue();
+ /** Helper to retrieve the --target id for the new AVD action. */
+ public int getCreateAvdTargetId() {
+ return ((Integer) getValue(VERB_CREATE, OBJECT_AVD, KEY_TARGET_ID)).intValue();
}
- /** Helper to retrieve the --name for the new vm action. */
- public String getNewVmName() {
- return ((String) getValue(ACTION_NEW_VM, KEY_NAME));
+ /** Helper to retrieve the --name for the new AVD action. */
+ public String getCreateAvdName() {
+ return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_NAME));
}
- /** Helper to retrieve the --skin name for the new vm action. */
- public String getNewVmSkin() {
- return ((String) getValue(ACTION_NEW_VM, KEY_SKIN));
+ /** Helper to retrieve the --skin name for the new AVD action. */
+ public String getCreateAvdSkin() {
+ return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SKIN));
}
- /** Helper to retrieve the --sdcard data for the new vm action. */
- public String getNewVmSdCard() {
- return ((String) getValue(ACTION_NEW_VM, KEY_SDCARD));
+ /** Helper to retrieve the --sdcard data for the new AVD action. */
+ public String getCreateAvdSdCard() {
+ return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SDCARD));
+ }
+
+ public boolean getCreateAvdForce() {
+ return ((Boolean) getValue(VERB_CREATE, OBJECT_AVD, KEY_FORCE)).booleanValue();
}
// -- some helpers for project action flags
/** Helper to retrieve the --out location for the new project action. */
- public String getNewProjectLocation() {
- return ((String) getValue(ACTION_NEW_PROJECT, KEY_OUT));
+ public String getCreateProjectLocation() {
+ return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PATH));
}
/** Helper to retrieve the --target id for the new project action. */
- public int getNewProjectTargetId() {
- return ((Integer) getValue(ACTION_NEW_PROJECT, KEY_TARGET_ID)).intValue();
+ public int getCreateProjectTargetId() {
+ return ((Integer) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue();
}
/** Helper to retrieve the --name for the new project action. */
- public String getNewProjectName() {
- return ((String) getValue(ACTION_NEW_PROJECT, KEY_NAME));
+ public String getCreateProjectName() {
+ return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_NAME));
}
/** Helper to retrieve the --package for the new project action. */
- public String getNewProjectPackage() {
- return ((String) getValue(ACTION_NEW_PROJECT, KEY_PACKAGE));
+ public String getCreateProjectPackage() {
+ return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PACKAGE));
}
/** Helper to retrieve the --activity for the new project action. */
- public String getNewProjectActivity() {
- return ((String) getValue(ACTION_NEW_PROJECT, KEY_ACTIVITY));
+ public String getCreateProjectActivity() {
+ return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_ACTIVITY));
}
// -- some helpers for update action flags
/** Helper to retrieve the --out location for the update project action. */
public String getUpdateProjectLocation() {
- return ((String) getValue(ACTION_UPDATE_PROJECT, KEY_OUT));
+ return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_PATH));
}
/** Helper to retrieve the --target id for the update project action. */
public int getUpdateProjectTargetId() {
- return ((Integer) getValue(ACTION_UPDATE_PROJECT, KEY_TARGET_ID)).intValue();
+ return ((Integer) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue();
}
/** Helper to retrieve the --name for the update project action. */
public String getUpdateProjectName() {
- return ((String) getValue(ACTION_UPDATE_PROJECT, KEY_NAME));
+ return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_NAME));
}
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java
index 1a82151..918591b 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java
@@ -38,20 +38,20 @@ public class CommandLineProcessorTest extends TestCase {
public MockCommandLineProcessor(ISdkLog logger) {
super(logger,
new String[][] {
- { "action1", "Some action" },
- { "action2", "Another action" },
+ { "verb1", "action1", "Some action" },
+ { "verb1", "action2", "Another action" },
});
define(MODE.STRING, false /*mandatory*/,
- "action1", "1", "first", "non-mandatory flag", null);
+ "verb1", "action1", "1", "first", "non-mandatory flag", null);
define(MODE.STRING, true /*mandatory*/,
- "action1", "2", "second", "mandatory flag", null);
+ "verb1", "action1", "2", "second", "mandatory flag", null);
}
@Override
- public void printHelpAndExitForAction(String actionFilter,
+ public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
mHelpCalled = true;
- super.printHelpAndExitForAction(actionFilter, errorFormat, args);
+ super.printHelpAndExitForAction(verb, directObject, errorFormat, args);
}
@Override
@@ -132,14 +132,14 @@ public class CommandLineProcessorTest extends TestCase {
assertTrue(c.isVerbose());
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
- assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
+ assertTrue(c.getStdErr().indexOf("Missing verb name.") != -1);
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "--verbose" });
assertTrue(c.isVerbose());
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
- assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
+ assertTrue(c.getStdErr().indexOf("Missing verb name.") != -1);
}
public final void testHelp() {
@@ -148,39 +148,39 @@ public class CommandLineProcessorTest extends TestCase {
c.parseArgs(new String[] { "-h" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
- assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
+ assertTrue(c.getStdErr().indexOf("Missing verb name.") == -1);
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "--help" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
- assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
+ assertTrue(c.getStdErr().indexOf("Missing verb name.") == -1);
}
public final void testMandatory() {
MockCommandLineProcessor c = new MockCommandLineProcessor(mLog);
- c.parseArgs(new String[] { "action1", "-1", "value1", "-2", "value2" });
+ c.parseArgs(new String[] { "verb1", "action1", "-1", "value1", "-2", "value2" });
assertFalse(c.wasExitCalled());
assertFalse(c.wasHelpCalled());
assertEquals("", c.getStdErr());
- assertEquals("value1", c.getValue("action1", "first"));
- assertEquals("value2", c.getValue("action1", "second"));
+ assertEquals("value1", c.getValue("verb1", "action1", "first"));
+ assertEquals("value2", c.getValue("verb1", "action1", "second"));
c = new MockCommandLineProcessor(mLog);
- c.parseArgs(new String[] { "action1", "-2", "value2" });
+ c.parseArgs(new String[] { "verb1", "action1", "-2", "value2" });
assertFalse(c.wasExitCalled());
assertFalse(c.wasHelpCalled());
assertEquals("", c.getStdErr());
- assertEquals(null, c.getValue("action1", "first"));
- assertEquals("value2", c.getValue("action1", "second"));
+ assertEquals(null, c.getValue("verb1", "action1", "first"));
+ assertEquals("value2", c.getValue("verb1", "action1", "second"));
c = new MockCommandLineProcessor(mLog);
- c.parseArgs(new String[] { "action1" });
+ c.parseArgs(new String[] { "verb1", "action1" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("must be defined") != -1);
- assertEquals(null, c.getValue("action1", "first"));
- assertEquals(null, c.getValue("action1", "second"));
+ assertEquals(null, c.getValue("verb1", "action1", "first"));
+ assertEquals(null, c.getValue("verb1", "action1", "second"));
}
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java
index 5a2c8e1..07a32e0 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java
@@ -37,10 +37,10 @@ public class SdkCommandLineTest extends TestCase {
}
@Override
- public void printHelpAndExitForAction(String actionFilter,
+ public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
mHelpCalled = true;
- super.printHelpAndExitForAction(actionFilter, errorFormat, args);
+ super.printHelpAndExitForAction(verb, directObject, errorFormat, args);
}
@Override
@@ -78,34 +78,64 @@ public class SdkCommandLineTest extends TestCase {
super.tearDown();
}
- /** Test list with long name and verbose */
- public final void testList_Long_Verbose() {
+ /** Test list */
+ public final void testList_Avd_Verbose() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
- assertEquals("all", c.getListFilter());
- c.parseArgs(new String[] { "-v", "list", "--filter", "vm" });
+ c.parseArgs(new String[] { "-v", "list", "avd" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
- assertEquals("vm", c.getListFilter());
+ assertEquals("list", c.getVerb());
+ assertEquals("avd", c.getDirectObject());
assertTrue(c.isVerbose());
}
- /** Test list with short name and no verbose */
- public final void testList_Short() {
+ public final void testList_Target() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
- assertEquals("all", c.getListFilter());
- c.parseArgs(new String[] { "list", "-f", "vm" });
+ c.parseArgs(new String[] { "list", "target" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
- assertEquals("vm", c.getListFilter());
+ assertEquals("list", c.getVerb());
+ assertEquals("target", c.getDirectObject());
+ assertFalse(c.isVerbose());
}
-
- /** Test list with long name and missing parameter */
- public final void testList_Long_MissingParam() {
+
+ public final void testList_None() {
+ MockSdkCommandLine c = new MockSdkCommandLine(mLog);
+ c.parseArgs(new String[] { "list" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("list", c.getVerb());
+ assertEquals("", c.getDirectObject());
+ assertFalse(c.isVerbose());
+ }
+
+ public final void testList_Invalid() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
- assertEquals("all", c.getListFilter());
- c.parseArgs(new String[] { "list", "--filter" });
+ c.parseArgs(new String[] { "list", "unknown" });
assertTrue(c.wasHelpCalled());
assertTrue(c.wasExitCalled());
- assertEquals("all", c.getListFilter());
+ assertEquals(null, c.getVerb());
+ assertEquals(null, c.getDirectObject());
+ assertFalse(c.isVerbose());
+ }
+
+ public final void testList_Plural() {
+ MockSdkCommandLine c = new MockSdkCommandLine(mLog);
+ c.parseArgs(new String[] { "list", "avds" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("list", c.getVerb());
+ // we get the non-plural form
+ assertEquals("avd", c.getDirectObject());
+ assertFalse(c.isVerbose());
+
+ c = new MockSdkCommandLine(mLog);
+ c.parseArgs(new String[] { "list", "targets" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("list", c.getVerb());
+ // we get the non-plural form
+ assertEquals("target", c.getDirectObject());
+ assertFalse(c.isVerbose());
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
index 8cbe44a..4894517 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
@@ -16,11 +16,48 @@
package com.android.sdklib;
+import java.util.Formatter;
+
/**
* Interface used to display warnings/errors while parsing the SDK content.
*/
public interface ISdkLog {
+
+ /**
+ * Prints a warning message on stdout.
+ * <p/>
+ * Implementations should only display warnings in verbose mode.
+ * The message should be prefixed with "Warning:".
+ *
+ * @param warningFormat is an optional error format. If non-null, it will be printed
+ * using a {@link Formatter} with the provided arguments.
+ * @param args provides the arguments for warningFormat.
+ */
void warning(String warningFormat, Object... args);
+
+ /**
+ * Prints an error message on stderr.
+ * <p/>
+ * Implementation should always display errors, independent of verbose mode.
+ * The message should be prefixed with "Error:".
+ *
+ * @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's
+ * message will be printed out.
+ * @param errorFormat is an optional error format. If non-null, it will be printed
+ * using a {@link Formatter} with the provided arguments.
+ * @param args provides the arguments for errorFormat.
+ */
void error(Throwable t, String errorFormat, Object... args);
+
+ /**
+ * Prints a message as-is on stdout.
+ * <p/>
+ * Implementation should always display errors, independent of verbose mode.
+ * No prefix is used, the message is printed as-is after formatting.
+ *
+ * @param msgFormat is an optional error format. If non-null, it will be printed
+ * using a {@link Formatter} with the provided arguments.
+ * @param args provides the arguments for msgFormat.
+ */
void printf(String msgFormat, Object... args);
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 39316d2..a43b8e6 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.vm;
+package com.android.sdklib.avd;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
@@ -39,12 +39,13 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * Virtual Machine manager to access the list of VMs or create new ones.
+ * Virtual Device Manager to access the list of AVDs or create new ones.
*/
-public final class VmManager {
+public final class AvdManager {
- private final static String VM_INFO_PATH = "path";
- private final static String VM_INFO_TARGET = "target";
+ private static final String AVD_FOLDER_EXTENSION = ".avd";
+ private final static String AVD_INFO_PATH = "path";
+ private final static String AVD_INFO_TARGET = "target";
private final static String IMAGE_USERDATA = "userdata.img";
private final static String CONFIG_INI = "config.ini";
@@ -54,7 +55,7 @@ public final class VmManager {
private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
- public static final class VmInfo {
+ public static final class AvdInfo {
String name;
String path;
IAndroidTarget target;
@@ -72,30 +73,30 @@ public final class VmManager {
}
}
- private final ArrayList<VmInfo> mVmList = new ArrayList<VmInfo>();
+ private final ArrayList<AvdInfo> mAvdList = new ArrayList<AvdInfo>();
private ISdkLog mSdkLog;
private final SdkManager mSdk;
- public VmManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
+ public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
mSdk = sdk;
mSdkLog = sdkLog;
- buildVmList();
+ buildAvdList();
}
/**
- * Returns the existing VMs.
- * @return a newly allocated arrays containing all the VMs.
+ * Returns the existing AVDs.
+ * @return a newly allocated array containing all the AVDs.
*/
- public VmInfo[] getVms() {
- return mVmList.toArray(new VmInfo[mVmList.size()]);
+ public AvdInfo[] getAvds() {
+ return mAvdList.toArray(new AvdInfo[mAvdList.size()]);
}
/**
- * Returns the {@link VmInfo} matching the given <var>name</var>.
- * @return the matching VmInfo or <code>null</code> if none were found.
+ * Returns the {@link AvdInfo} matching the given <var>name</var>.
+ * @return the matching AvdInfo or <code>null</code> if none were found.
*/
- public VmInfo getVm(String name) {
- for (VmInfo info : mVmList) {
+ public AvdInfo getAvd(String name) {
+ for (AvdInfo info : mAvdList) {
if (info.name.equals(name)) {
return info;
}
@@ -105,19 +106,20 @@ public final class VmManager {
}
/**
- * Creates a new VM. It is expected that there is no existing VM with this name already.
- * @param parentFolder the folder to contain the VM. A new folder will be created in this
- * folder with the name of the VM
- * @param name the name of the VM
- * @param target the target of the VM
+ * Creates a new AVD. It is expected that there is no existing AVD with this name already.
+ * @param parentFolder the folder to contain the AVD. A new folder will be created in this
+ * folder with the name of the AVD
+ * @param name the name of the AVD
+ * @param target the target of the AVD
* @param skinName the name of the skin. Can be null.
* @param sdcard the parameter value for the sdCard. Can be null. This is either a path to
* an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
- * @param hardwareConfig the hardware setup for the VM
+ * @param hardwareConfig the hardware setup for the AVD
+ * @param removePrevious If true remove any previous files.
*/
- public VmInfo createVm(String parentFolder, String name, IAndroidTarget target,
+ public AvdInfo createAvd(String parentFolder, String name, IAndroidTarget target,
String skinName, String sdcard, Map<String,String> hardwareConfig,
- ISdkLog log) {
+ boolean removePrevious, ISdkLog log) {
try {
File rootDirectory = new File(parentFolder);
@@ -128,24 +130,31 @@ public final class VmManager {
return null;
}
- File vmFolder = new File(parentFolder, name + ".avm");
- if (vmFolder.exists()) {
- if (log != null) {
- log.error(null, "Folder %s is in the way.", vmFolder.getAbsolutePath());
+ File avdFolder = new File(parentFolder, name + AVD_FOLDER_EXTENSION);
+ if (avdFolder.exists()) {
+ if (removePrevious) {
+ // AVD already exists and removePrevious is set, try to remove the
+ // directory's content first (but not the directory itself).
+ recursiveDelete(avdFolder);
+ } else {
+ // AVD shouldn't already exist if removePrevious is false.
+ if (log != null) {
+ log.error(null, "Folder %s is in the way.", avdFolder.getAbsolutePath());
+ }
+ return null;
}
- return null;
}
- // create the vm folder.
- vmFolder.mkdir();
+ // create the AVD folder.
+ avdFolder.mkdir();
HashMap<String, String> values = new HashMap<String, String>();
// prepare the ini file.
- String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
- File iniFile = new File(vmRoot, name + ".ini");
- values.put(VM_INFO_PATH, vmFolder.getAbsolutePath());
- values.put(VM_INFO_TARGET, target.hashString());
+ String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
+ File iniFile = new File(avdRoot, name + ".ini");
+ values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
+ values.put(AVD_INFO_TARGET, target.hashString());
createConfigIni(iniFile, values);
// writes the userdata.img in it.
@@ -153,7 +162,7 @@ public final class VmManager {
File userdataSrc = new File(imagePath, IMAGE_USERDATA);
FileInputStream fis = new FileInputStream(userdataSrc);
- File userdataDest = new File(vmFolder, IMAGE_USERDATA);
+ File userdataDest = new File(avdFolder, IMAGE_USERDATA);
FileOutputStream fos = new FileOutputStream(userdataDest);
byte[] buffer = new byte[4096];
@@ -193,7 +202,7 @@ public final class VmManager {
Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
if (m.matches()) {
// create the sdcard.
- sdcardFile = new File(vmFolder, "sdcard.img");
+ sdcardFile = new File(avdFolder, "sdcard.img");
String path = sdcardFile.getAbsolutePath();
// execute mksdcard with the proper parameters.
@@ -224,28 +233,27 @@ public final class VmManager {
values.putAll(hardwareConfig);
}
- File configIniFile = new File(vmFolder, CONFIG_INI);
+ File configIniFile = new File(avdFolder, CONFIG_INI);
createConfigIni(configIniFile, values);
if (log != null) {
if (target.isPlatform()) {
- log.printf("Created VM '%s' based on %s\n", name, target.getName());
+ log.printf("Created AVD '%s' based on %s\n", name, target.getName());
} else {
- log.printf(
- "Created VM '%s' based on %s (%s)\n", name, target.getName(),
- target.getVendor());
+ log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(),
+ target.getVendor());
}
}
- // create the VmInfo object, and add it to the list
- VmInfo vmInfo = new VmInfo();
- vmInfo.name = name;
- vmInfo.path = vmFolder.getAbsolutePath();
- vmInfo.target = target;
+ // create the AvdInfo object, and add it to the list
+ AvdInfo avdInfo = new AvdInfo();
+ avdInfo.name = name;
+ avdInfo.path = avdFolder.getAbsolutePath();
+ avdInfo.target = target;
- mVmList.add(vmInfo);
+ mAvdList.add(avdInfo);
- return vmInfo;
+ return avdInfo;
} catch (AndroidLocationException e) {
if (log != null) {
log.error(e, null);
@@ -259,21 +267,35 @@ public final class VmManager {
return null;
}
- private void buildVmList() throws AndroidLocationException {
+ /**
+ * Helper method to recursively delete a folder's content (but not the folder itself).
+ *
+ * @throws SecurityException like {@link File#delete()} does if file/folder is not writable.
+ */
+ public void recursiveDelete(File folder) {
+ for (File f : folder.listFiles()) {
+ if (f.isDirectory()) {
+ recursiveDelete(folder);
+ }
+ f.delete();
+ }
+ }
+
+ private void buildAvdList() throws AndroidLocationException {
// get the Android prefs location.
- String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
+ String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
// ensure folder validity.
- File folder = new File(vmRoot);
+ File folder = new File(avdRoot);
if (folder.isFile()) {
- throw new AndroidLocationException(String.format("%s is not a valid folder.", vmRoot));
+ throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
// folder is not there, we create it and return
folder.mkdirs();
return;
}
- File[] vms = folder.listFiles(new FilenameFilter() {
+ File[] avds = folder.listFiles(new FilenameFilter() {
public boolean accept(File parent, String name) {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
@@ -284,23 +306,23 @@ public final class VmManager {
}
});
- for (File vm : vms) {
- VmInfo info = parseVmInfo(vm);
+ for (File avd : avds) {
+ AvdInfo info = parseAvdInfo(avd);
if (info != null) {
- mVmList.add(info);
+ mAvdList.add(info);
}
}
}
- private VmInfo parseVmInfo(File path) {
+ private AvdInfo parseAvdInfo(File path) {
Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
- String vmPath = map.get(VM_INFO_PATH);
- if (vmPath == null) {
+ String avdPath = map.get(AVD_INFO_PATH);
+ if (avdPath == null) {
return null;
}
- String targetHash = map.get(VM_INFO_TARGET);
+ String targetHash = map.get(AVD_INFO_TARGET);
if (targetHash == null) {
return null;
}
@@ -310,14 +332,14 @@ public final class VmManager {
return null;
}
- VmInfo info = new VmInfo();
+ AvdInfo info = new AvdInfo();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
if (matcher.matches()) {
info.name = matcher.group(1);
} else {
info.name = path.getName(); // really this should not happen.
}
- info.path = vmPath;
+ info.path = avdPath;
info.target = target;
return info;
@@ -447,4 +469,14 @@ public final class VmManager {
return process.waitFor();
}
+ /**
+ * Removes an {@link AvdInfo} from the internal list.
+ *
+ * @param avdInfo The {@link AvdInfo} to remove.
+ * @return true if this {@link AvdInfo} was present and has been removed.
+ */
+ public boolean removeAvd(AvdInfo avdInfo) {
+ return mAvdList.remove(avdInfo);
+ }
+
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/HardwareProperties.java
index 98e97fe..ed5b949 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/HardwareProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.vm;
+package com.android.sdklib.avd;
import com.android.sdklib.ISdkLog;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
index dcc0b9e..4a0ee06 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
@@ -17,7 +17,7 @@
package com.android.sdkuilib;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.vm.VmManager.VmInfo;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
@@ -40,16 +40,16 @@ import java.util.ArrayList;
/**
- * The VM selector is a table that is added to the given parent composite.
+ * The AVD selector is a table that is added to the given parent composite.
* <p/>
- * To use, create it using {@link #VmSelector(Composite, VmInfo[], boolean)} then
- * call {@link #setSelection(VmInfo)}, {@link #setSelectionListener(SelectionListener)}
+ * To use, create it using {@link #AvdSelector(Composite, AvdInfo[], boolean)} then
+ * call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)}
* and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the
* selection.
*/
-public final class VmSelector {
+public final class AvdSelector {
- private VmInfo[] mVms;
+ private AvdInfo[] mAvds;
private final boolean mAllowMultipleSelection;
private SelectionListener mSelectionListener;
private Table mTable;
@@ -59,12 +59,12 @@ public final class VmSelector {
* Creates a new SDK Target Selector.
*
* @param parent The parent composite where the selector will be added.
- * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
+ * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* @param allowMultipleSelection True if more than one SDK target can be selected at the same
* time.
*/
- public VmSelector(Composite parent, VmInfo[] vms, boolean allowMultipleSelection) {
- mVms = vms;
+ public AvdSelector(Composite parent, AvdInfo[] avds, boolean allowMultipleSelection) {
+ mAvds = avds;
// Layout has 1 column
Composite group = new Composite(parent, SWT.NONE);
@@ -89,7 +89,7 @@ public final class VmSelector {
// create the table columns
final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
- column0.setText("VM Name");
+ column0.setText("AVD Name");
final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
column1.setText("Target Name");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
@@ -104,25 +104,25 @@ public final class VmSelector {
}
/**
- * Sets a new set of VM, with an optional filter.
+ * Sets a new set of AVD, with an optional filter.
* <p/>This must be called from the UI thread.
*
- * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
- * @param filter An IAndroidTarget. If non-null, only VM whose target are compatible with the
+ * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
+ * @param filter An IAndroidTarget. If non-null, only AVD whose target are compatible with the
* filter target will displayed an available for selection.
*/
- public void setVms(VmInfo[] vms, IAndroidTarget filter) {
- mVms = vms;
+ public void setAvds(AvdInfo[] avds, IAndroidTarget filter) {
+ mAvds = avds;
fillTable(mTable, filter);
}
/**
- * Returns the list of known Vms.
+ * Returns the list of known AVDs.
* <p/>
* This is not a copy. Callers must <em>not</em> modify this array.
*/
- public VmInfo[] getVms() {
- return mVms;
+ public AvdInfo[] getAvds() {
+ return mAvds;
}
/**
@@ -151,11 +151,11 @@ public final class VmSelector {
* @param target the target to be selection
* @return true if the target could be selected, false otherwise.
*/
- public boolean setSelection(VmInfo target) {
+ public boolean setSelection(AvdInfo target) {
boolean found = false;
boolean modified = false;
for (TableItem i : mTable.getItems()) {
- if ((VmInfo) i.getData() == target) {
+ if ((AvdInfo) i.getData() == target) {
found = true;
if (!i.getChecked()) {
modified = true;
@@ -181,14 +181,14 @@ public final class VmSelector {
* @see #getFirstSelected()
* @return An array of selected items. The list can be empty but not null.
*/
- public VmInfo[] getAllSelected() {
+ public AvdInfo[] getAllSelected() {
ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
list.add((IAndroidTarget) i.getData());
}
}
- return list.toArray(new VmInfo[list.size()]);
+ return list.toArray(new AvdInfo[list.size()]);
}
/**
@@ -198,10 +198,10 @@ public final class VmSelector {
* @see #getAllSelected()
* @return The first selected item or null.
*/
- public VmInfo getFirstSelected() {
+ public AvdInfo getFirstSelected() {
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
- return (VmInfo) i.getData();
+ return (AvdInfo) i.getData();
}
}
return null;
@@ -283,7 +283,7 @@ public final class VmSelector {
}
/**
- * Fills the table with all VM.
+ * Fills the table with all AVD.
* The table columns are:
* <ul>
* <li>column 0: sdk name
@@ -294,14 +294,14 @@ public final class VmSelector {
*/
private void fillTable(final Table table, IAndroidTarget filter) {
table.removeAll();
- if (mVms != null && mVms.length > 0) {
+ if (mAvds != null && mAvds.length > 0) {
table.setEnabled(true);
- for (VmInfo vm : mVms) {
- if (filter == null || filter.isCompatibleBaseFor(vm.getTarget())) {
+ for (AvdInfo avd : mAvds) {
+ if (filter == null || filter.isCompatibleBaseFor(avd.getTarget())) {
TableItem item = new TableItem(table, SWT.NONE);
- item.setData(vm);
- item.setText(0, vm.getName());
- IAndroidTarget target = vm.getTarget();
+ item.setData(avd);
+ item.setText(0, avd.getName());
+ IAndroidTarget target = avd.getTarget();
item.setText(1, target.getFullName());
item.setText(2, target.getApiVersionName());
item.setText(3, Integer.toString(target.getApiVersionNumber()));
@@ -314,7 +314,7 @@ public final class VmSelector {
TableItem item = new TableItem(table, SWT.NONE);
item.setData(null);
item.setText(0, "--");
- item.setText(1, "No VM available");
+ item.setText(1, "No AVD available");
item.setText(2, "--");
item.setText(3, "--");
}
@@ -365,13 +365,13 @@ public final class VmSelector {
}
/**
- * Updates the description label with the path of the item's VM, if any.
+ * Updates the description label with the path of the item's AVD, if any.
*/
private void updateDescription(TableItem item) {
if (item != null) {
Object data = item.getData();
- if (data instanceof VmInfo) {
- String newTooltip = ((VmInfo) data).getPath();
+ if (data instanceof AvdInfo) {
+ String newTooltip = ((AvdInfo) data).getPath();
mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
}
}