Commit 6c36d5aa authored by Evan W. Patton's avatar Evan W. Patton

Add support for multiple extensions per AIX

For the Internet of Things update, we want to be able to have
collections of related extensions, for example, targetting different
features of the same platform, to be imported as a single unit. This
behavior reflects how we group components such as the LEGO EV3 or NXT
robots and allows us to treat extensions in the same way.

Change-Id: I83e1069421939e57b33689f7ae0a3d5928c6c6cd
parent dea242fe
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2017 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -685,10 +685,8 @@ public abstract class MockComponent extends Composite implements PropertyChangeL
// Note: We create a ClippedImagePrototype because we need something that can be
// used to get HTML for the iconImage. AbstractImagePrototype requires
// an ImageResource, which we don't necessarily have.
String imageHTML = new ClippedImagePrototype(iconImage.getUrl(), iconImage.getOriginLeft(),
iconImage.getOriginTop(), ICON_IMAGE_WIDTH, ICON_IMAGE_HEIGHT).getHTML();
TreeItem itemNode = new TreeItem(
new HTML("<span>" + imageHTML + getName() + "</span>"));
new HTML("<span>" + iconImage.getElement().getString() + getName() + "</span>"));
itemNode.setUserObject(sourceStructureExplorerItem);
return itemNode;
}
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2017 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -41,6 +41,7 @@ import com.google.appinventor.client.editor.simple.components.MockVerticalArrang
import com.google.appinventor.client.editor.simple.components.MockVideoPlayer;
import com.google.appinventor.client.editor.simple.components.MockWebViewer;
import com.google.appinventor.shared.storage.StorageUtil;
import com.google.common.collect.Maps;
import com.google.gwt.resources.client.ImageResource;
......@@ -228,7 +229,10 @@ public final class SimpleComponentDescriptor {
*/
public Image getImage() {
if (nonVisible) {
return getImageFromPath(COMPONENT_DATABASE.getIconName(name));
String type = COMPONENT_DATABASE.getComponentType(name);
return getImageFromPath(COMPONENT_DATABASE.getIconName(name),
type.substring(0, type.lastIndexOf('.')),
editor.getProjectId());
} else {
return getCachedMockComponent(name, editor).getIconImage();
}
......@@ -241,7 +245,7 @@ public final class SimpleComponentDescriptor {
* @return draggable widget for component
*/
public Widget getDragWidget() {
return createMockComponent(name, editor);
return createMockComponent(name, COMPONENT_DATABASE.getComponentType(name), editor);
}
/**
......@@ -250,7 +254,8 @@ public final class SimpleComponentDescriptor {
* @return mock component
*/
public MockComponent createMockComponentFromPalette() {
MockComponent mockComponent = createMockComponent(name, editor);
MockComponent mockComponent = createMockComponent(name,
COMPONENT_DATABASE.getComponentType(name), editor);
mockComponent.onCreateFromPalette();
return mockComponent;
}
......@@ -260,15 +265,24 @@ public final class SimpleComponentDescriptor {
*/
private MockComponent getCachedMockComponent(String name, SimpleEditor editor) {
if (cachedMockComponent == null) {
cachedMockComponent = createMockComponent(name, editor);
cachedMockComponent = createMockComponent(name,
COMPONENT_DATABASE.getComponentType(name), editor);
}
return cachedMockComponent;
}
public static Image getImageFromPath(String iconPath) {
public static Image getImageFromPath(String iconPath, String packageName, long projectId) {
if (!imagesInitialized) {
initBundledImages();
}
if (iconPath.startsWith("aiwebres/") && packageName != null) {
// icon for extension
Image image = new Image(StorageUtil.getFileUrl(projectId,
"assets/external_comps/" + packageName + "/" + iconPath));
image.setWidth("16px");
image.setHeight("16px");
return image;
}
if (bundledImages.containsKey(iconPath)) {
return new Image(bundledImages.get(iconPath));
} else {
......@@ -279,14 +293,17 @@ public final class SimpleComponentDescriptor {
/**
* Instantiates mock component by name.
*/
public static MockComponent createMockComponent(String name, SimpleEditor editor) {
public static MockComponent createMockComponent(String name, String type, SimpleEditor editor) {
if (SimpleComponentDatabase.getInstance(editor.getProjectId()).getNonVisible(name)) {
if(name.equals(MockFirebaseDB.TYPE)) {
return new MockFirebaseDB(editor, name,
getImageFromPath(SimpleComponentDatabase.getInstance(editor.getProjectId()).getIconName(name)));
getImageFromPath(SimpleComponentDatabase.getInstance(editor.getProjectId()).getIconName(name),
null, editor.getProjectId()));
} else {
String pkgName = type.contains(".") ? type.substring(0, type.lastIndexOf('.')) : null;
return new MockNonVisibleComponent(editor, name,
getImageFromPath(SimpleComponentDatabase.getInstance(editor.getProjectId()).getIconName(name)));
getImageFromPath(SimpleComponentDatabase.getInstance(editor.getProjectId()).getIconName(name),
pkgName, editor.getProjectId()));
}
} else if (name.equals(MockButton.TYPE)) {
return new MockButton(editor);
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2017 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -529,7 +529,8 @@ public final class YaFormEditor extends SimpleEditor implements FormChangeListen
// Instantiate new root component
mockComponent = new MockForm(this);
} else {
mockComponent = SimpleComponentDescriptor.createMockComponent(componentType, this);
mockComponent = SimpleComponentDescriptor.createMockComponent(componentType,
COMPONENT_DATABASE.getComponentType(componentType), this);
// Add the component to its parent component (and if it is non-visible, add it to the
// nonVisibleComponent panel).
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2017 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -23,7 +23,9 @@ import com.google.appinventor.client.explorer.project.ProjectChangeListener;
import com.google.appinventor.client.output.OdeLog;
import com.google.appinventor.client.properties.json.ClientJsonParser;
import com.google.appinventor.common.utils.StringUtils;
import com.google.appinventor.shared.properties.json.JSONArray;
import com.google.appinventor.shared.properties.json.JSONObject;
import com.google.appinventor.shared.properties.json.JSONValue;
import com.google.appinventor.shared.rpc.project.ChecksumedFileException;
import com.google.appinventor.shared.rpc.project.ChecksumedLoadFile;
import com.google.appinventor.shared.rpc.project.ProjectNode;
......@@ -44,8 +46,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Project editor for Young Android projects. Each instance corresponds to
......@@ -74,6 +78,12 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
// List of External Components
private final List<String> externalComponents = new ArrayList<String>();
// Mapping of package names to extensions defined by the package (n > 1)
private final Map<String, Set<String>> externalCollections = new HashMap<>();
// Number of external component descriptors loaded since there is no longer a 1-1 correspondence
private volatile int numExternalComponentsLoaded = 0;
// List of ComponentDatabaseChangeListeners
private final List<ComponentDatabaseChangeListener> componentDatabaseChangeListeners = new ArrayList<ComponentDatabaseChangeListener>();
......@@ -471,12 +481,34 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
this.onFailure(e);
return;
}
JSONObject componentJSONObject = new ClientJsonParser().parse(jsonFileContent).asObject();
JSONValue value = new ClientJsonParser().parse(jsonFileContent);
COMPONENT_DATABASE.addComponentDatabaseListener(YaProjectEditor.this);
COMPONENT_DATABASE.addComponent(componentJSONObject);
if (!externalComponents.contains(componentJSONObject.get("name").toString())) { // In case of upgrade, we do not need to add entry
externalComponents.add(componentJSONObject.get("name").toString());
if (value instanceof JSONArray) {
JSONArray componentList = value.asArray();
COMPONENT_DATABASE.addComponents(componentList);
for (JSONValue component : componentList.getElements()) {
String name = component.asObject().get("type").asString().getString();
if (componentList.size() > 1) { // this is a extension collection
String packageName = name.substring(0, name.lastIndexOf('.'));
if (!externalCollections.containsKey(packageName)) {
externalCollections.put(packageName, new HashSet<String>());
}
externalCollections.get(packageName).add(name);
name = packageName;
}
if (!externalComponents.contains(name)) {
externalComponents.add(name);
}
}
} else {
JSONObject componentJSONObject = value.asObject();
COMPONENT_DATABASE.addComponent(componentJSONObject);
// In case of upgrade, we do not need to add entry
if (!externalComponents.contains(componentJSONObject.get("type").toString())) {
externalComponents.add(componentJSONObject.get("type").toString());
}
}
numExternalComponentsLoaded++;
if (afterComponentAdded != null) {
afterComponentAdded.execute();
}
......@@ -499,8 +531,17 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
public void removeComponent(Map<String, String> componentTypes) {
final Ode ode = Ode.getInstance();
final YoungAndroidComponentsFolder componentsFolder = ((YoungAndroidProjectNode) project.getRootNode()).getComponentsFolder();
Set<String> removedPackages = new HashSet<String>();
for (String componentType : componentTypes.keySet()) {
final String directory = componentsFolder.getFileId() + "/" + componentTypes.get(componentType) + "/";
String typeName = componentTypes.get(componentType);
if (!externalComponents.contains(typeName)) {
typeName = typeName.substring(0, typeName.lastIndexOf('.'));
if (removedPackages.contains(typeName)) {
continue;
}
removedPackages.add(typeName);
}
final String directory = componentsFolder.getFileId() + "/" + typeName + "/";
ode.getProjectService().deleteFolder(ode.getSessionId(), this.projectId, directory,
new AsyncCallback<Long>() {
@Override
......@@ -552,7 +593,7 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
addComponent(componentNode, new Command() {
@Override
public void execute() {
if (componentCount == externalComponents.size()) { // This will be true for the last component added
if (componentCount == numExternalComponentsLoaded) { // true for the last component added
externalComponentsLoaded = true;
}
}
......@@ -567,6 +608,7 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
COMPONENT_DATABASE.addComponentDatabaseListener(this);
COMPONENT_DATABASE.resetDatabase();
externalComponents.clear();
numExternalComponentsLoaded = 0;
}
private static boolean isScreen1(String formName) {
......@@ -601,6 +643,17 @@ public final class YaProjectEditor extends ProjectEditor implements ProjectChang
@Override
public boolean beforeComponentTypeRemoved(List<String> componentTypes) {
boolean result = true;
Set<String> removedTypes = new HashSet<>(componentTypes);
// aggregate types in the same package
for (String type : removedTypes) {
String packageName = COMPONENT_DATABASE.getComponentType(type);
packageName = packageName.substring(0, packageName.lastIndexOf('.'));
if (externalCollections.containsKey(packageName)) {
for (String siblingType : externalCollections.get(packageName)) {
componentTypes.add(siblingType.substring(siblingType.lastIndexOf('.') + 1));
}
}
}
for (ComponentDatabaseChangeListener cdbChangeListener : componentDatabaseChangeListeners) {
result = result & cdbChangeListener.beforeComponentTypeRemoved(componentTypes);
}
......
......@@ -79,7 +79,8 @@ public class ComponentImportWizard extends Wizard {
if (project == null) {
return; // Project does not exist!
}
if (response.getStatus() == ComponentImportResponse.Status.UPGRADED) {
if (response.getStatus() == ComponentImportResponse.Status.UPGRADED ||
response.getStatus() == ComponentImportResponse.Status.IMPORTED) {
YoungAndroidComponentsFolder componentsFolder = ((YoungAndroidProjectNode) project.getRootNode()).getComponentsFolder();
YaProjectEditor projectEditor = (YaProjectEditor) ode.getEditorManager().getOpenProjectEditor(destinationProjectId);
if (projectEditor == null) {
......@@ -87,26 +88,12 @@ public class ComponentImportWizard extends Wizard {
}
for (ProjectNode node : compNodes) {
project.addNode(componentsFolder, node);
if (node.getName().equals("component.json") && StringUtils.countMatches(node.getFileId(), "/") == 3) {
if ((node.getName().equals("component.json") || node.getName().equals("components.json"))
&& StringUtils.countMatches(node.getFileId(), "/") == 3) {
projectEditor.addComponent(node, null);
}
}
} else if (response.getStatus() == ComponentImportResponse.Status.IMPORTED) {
for (ProjectNode node : compNodes) {
if (node.getName().equals("component.json") && StringUtils.countMatches(node.getFileId(), "/") == 3) {
String fileId = node.getFileId();
int start = fileId.indexOf(external_components) + external_components.length();
int end = fileId.indexOf('/', start);
String typeName = fileId.substring(start, end);
new ComponentRenameWizard(typeName, destinationProjectId, compNodes).center();
}
}
}
}
}
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2015 MIT, All rights reserved
// Copyright 2015-2017 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -11,7 +11,6 @@ import com.google.appinventor.shared.rpc.component.ComponentImportResponse;
import com.google.appinventor.shared.rpc.project.youngandroid.YoungAndroidComponentNode;
import com.google.appinventor.shared.storage.StorageUtil;
import com.google.appinventor.shared.rpc.BlocksTruncatedException;
import com.google.appinventor.shared.rpc.component.Component;
import com.google.appinventor.shared.rpc.component.ComponentService;
import com.google.appinventor.shared.rpc.project.FileNode;
import com.google.appinventor.shared.rpc.project.ProjectNode;
......@@ -25,12 +24,14 @@ import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.json.JSONArray;
import org.json.JSONObject;
public class ComponentServiceImpl extends OdeRemoteServiceServlet
......@@ -131,28 +132,102 @@ public class ComponentServiceImpl extends OdeRemoteServiceServlet
return contents;
}
/**
* Updates the name of any components in newComponents with the name specified in oldComponents.
* This destructively modifies newComponents.
* @param oldComponents an array of component descriptions from the old component[s].json
* @param newComponents an array of component descriptions from the new component[s].json
* @return A mapping of component fully-qualified class names to presentation (UI) names
*/
private Map<String, String> applyRenames(JSONArray oldComponents, JSONArray newComponents) {
Map<String, String> types = new HashMap<>();
for (int i = 0; i < oldComponents.length(); i++) {
JSONObject component = oldComponents.getJSONObject(i);
types.put(component.getString("type"), component.getString("name"));
}
for (int i = 0; i < newComponents.length(); i++) {
JSONObject component = newComponents.getJSONObject(i);
String type = component.getString("type");
if (!types.containsKey(type)) {
types.put(type, component.getString("name"));
} else {
component.put("name", types.get(type));
}
}
return types;
}
/**
* Extract a mapping of component fully-quallyified class names to presentation (UI) names
* @param components an array of component descriptions
* @return A mapping of component fully-qualified class names to presentation (UI) names
*/
private Map<String, String> extractTypes(JSONArray components) {
Map<String, String> types = new HashMap<>();
for (int i = 0; i < components.length(); i++) {
JSONObject component = components.getJSONObject(i);
types.put(component.getString("type"), component.getString("name"));
}
return types;
}
private void importToProject(Map<String, byte[]> contents, long projectId,
String folderPath, ComponentImportResponse response) throws FileImporterException, IOException {
response.setStatus(ComponentImportResponse.Status.IMPORTED);
List<ProjectNode> compNodes = new ArrayList<ProjectNode>();
response.setProjectId(projectId);
List<String> sourceFiles = storageIo.getProjectSourceFiles(userInfoProvider.getUserId(), projectId);
boolean extensionUpgrade = false;
String oldCompName; // name with which extension is already imported, we have to grab from the old component files
Map<String, String> types = new TreeMap<>();
for (String name : contents.keySet()) {
String destination = folderPath + "/external_comps/" + name;
if (sourceFiles.contains(destination)) { // Check if source File already contains component files
// This is an upgrade, if it replaces old component files
extensionUpgrade = true;
response.setStatus(ComponentImportResponse.Status.UPGRADED);
JSONArray oldComponents, newComponents;
if (StorageUtil.basename(name).equals("component.json")) { // TODO : we need a more secure check
// We modify the name property of the new component.json to match that of the already imported component.json
JSONObject oldCompJson = new JSONObject(storageIo.downloadFile(
userInfoProvider.getUserId(), projectId, destination, StorageUtil.DEFAULT_CHARSET));
oldCompName = oldCompJson.getString("name");
String componentJSONString = new String(contents.get(name), StorageUtil.DEFAULT_CHARSET);
JSONObject newCompJSon = new JSONObject(componentJSONString);
newCompJSon.put("name", oldCompName); //change the name to the same as that of already imported component
componentJSONString = newCompJSon.toString(1); // 1 is the indent factor, let it look beautiful
contents.put(name, componentJSONString.getBytes(StorageUtil.DEFAULT_CHARSET));
oldComponents = new JSONArray("[" + storageIo.downloadFile(userInfoProvider.getUserId(), projectId, destination, StorageUtil.DEFAULT_CHARSET));
newComponents = new JSONArray("[" + new String(contents.get(name), StorageUtil.DEFAULT_CHARSET) + "]");
types = applyRenames(oldComponents, newComponents);
// upgrade component.json to components.json
contents.remove(name);
name = name.substring(0, name.length() - "component.json".length()) + "components.json";
contents.put(name, newComponents.toString().getBytes(StorageUtil.DEFAULT_CHARSET));
} else if (StorageUtil.basename(name).equals("components.json")) {
oldComponents = new JSONArray(storageIo.downloadFile(userInfoProvider.getUserId(), projectId, destination, StorageUtil.DEFAULT_CHARSET));
newComponents = new JSONArray(new String(contents.get(name), StorageUtil.DEFAULT_CHARSET));
types = applyRenames(oldComponents, newComponents);
contents.put(name, newComponents.toString().getBytes(StorageUtil.DEFAULT_CHARSET));
}
} else if(StorageUtil.basename(name).equals("components.json")) {
String oldDestination = destination.substring(0, destination.length() - "components.json".length()) + "component.json";
if (sourceFiles.contains(oldDestination)) {
// old extension used component.json but new extension is components.json
JSONArray oldComponents, newComponents;
oldComponents = new JSONArray("[" + storageIo.downloadFile(userInfoProvider.getUserId(), projectId, destination, StorageUtil.DEFAULT_CHARSET) + "]");
newComponents = new JSONArray(new String(contents.get(name), StorageUtil.DEFAULT_CHARSET));
types = applyRenames(oldComponents, newComponents);
storageIo.deleteFile(userInfoProvider.getUserId(), projectId, oldDestination);
} else {
// new file
types = extractTypes(new JSONArray(new String(contents.get(name), StorageUtil.DEFAULT_CHARSET)));
}
} else if(StorageUtil.basename(name).equals("component.json")) {
String altDestination = destination.substring(0, destination.length() - "component.json".length()) + "components.json";
String arrayContent = "[" + new String(contents.get(name), StorageUtil.DEFAULT_CHARSET) + "]";
if (sourceFiles.contains(altDestination)) {
// potential downgrade? new extensions have components.json
types = applyRenames(new JSONArray(storageIo.downloadFile(userInfoProvider.getUserId(), projectId, destination, StorageUtil.DEFAULT_CHARSET)), new JSONArray(arrayContent));
// upgrade component.json to components.json
contents.remove(name);
contents.put(altDestination, arrayContent.getBytes(StorageUtil.DEFAULT_CHARSET));
name = altDestination;
} else {
// new file; force upgrade to components.json
types = extractTypes(new JSONArray(arrayContent));
// upgrade component.json to components.json
contents.remove(name);
contents.put(altDestination, arrayContent.getBytes(StorageUtil.DEFAULT_CHARSET));
name = altDestination;
}
}
FileNode fileNode = new YoungAndroidComponentNode(StorageUtil.basename(name), destination);
......@@ -161,16 +236,8 @@ public class ComponentServiceImpl extends OdeRemoteServiceServlet
compNodes.add(fileNode);
}
if (extensionUpgrade) {
response.setStatus(ComponentImportResponse.Status.UPGRADED);
} else {
response.setStatus(ComponentImportResponse.Status.IMPORTED);
}
String type = contents.keySet().iterator().next(); // get an element
type = type.substring(0, type.indexOf('/')); // get the type
response.setComponentType(type);
response.setComponentTypes(types);
response.setNodes(compNodes);
return;
}
......
......@@ -9,7 +9,9 @@ import com.google.appinventor.shared.rpc.project.ProjectNode;
import com.google.gwt.user.client.rpc.IsSerializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Component Import Response to Send Failure Reasons
......@@ -26,18 +28,18 @@ public class ComponentImportResponse implements IsSerializable{
private Status status;
private long projectId; // necessary to ensure right project
private String componentType; // Type of Component
private List<ProjectNode> nodes; // Added Nodes
private Map<String, String> types;
public ComponentImportResponse(Status status, long projectId, String componentType, List<ProjectNode> nodes) {
public ComponentImportResponse(Status status, long projectId, Map<String, String> types, List<ProjectNode> nodes) {
this.status = status;
this.projectId = projectId;
this.componentType = componentType;
this.nodes = nodes;
this.types = types;
}
public ComponentImportResponse(Status status) {
this(status, 0, "", null);
this(status, 0, new HashMap<String, String>(), null);
}
private ComponentImportResponse() {
......@@ -59,12 +61,21 @@ public class ComponentImportResponse implements IsSerializable{
this.projectId = projectId;
}
/**
* Use {@link #getComponentTypes()} instead.
* @return
*/
@Deprecated
public String getComponentType() {
return componentType;
return types.keySet().iterator().next();
}
public void setComponentType(String componentType) {
this.componentType = componentType;
public Map<String, String> getComponentTypes() {
return types;
}
public void setComponentTypes(Map<String, String> types) {
this.types = types;
}
public List<ProjectNode> getNodes() {
......
......@@ -19,6 +19,7 @@ import com.android.sdklib.build.ApkBuilder;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.codehaus.jettison.json.JSONTokener;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
......@@ -1556,11 +1557,26 @@ public final class Compiler {
for (String type : extCompTypes) {
// .../assets/external_comps/com.package.MyExtComp/files/component_build_info.json
File extCompRuntimeFileDir = new File(getExtCompDirPath(type) + RUNTIME_FILES_DIR);
if (!extCompRuntimeFileDir.exists()) {
// try extension package name for multi-extension files
String path = getExtCompDirPath(type);
path = path.substring(0, path.lastIndexOf('.'));
extCompRuntimeFileDir = new File(path + RUNTIME_FILES_DIR);
}
String jsonFileName = "component_build_info.json";
File jsonFile = new File(extCompRuntimeFileDir, jsonFileName);
extCompsBuildInfo.put(new JSONObject(Resources.toString(
jsonFile.toURI().toURL(), Charsets.UTF_8)));
String buildInfo = Resources.toString(jsonFile.toURI().toURL(), Charsets.UTF_8);
JSONTokener tokener = new JSONTokener(buildInfo);
Object value = tokener.nextValue();
if (value instanceof JSONObject) {
extCompsBuildInfo.put((JSONObject) value);
} else if (value instanceof JSONArray) {
JSONArray infos = (JSONArray) value;
for (int i = 0; i < infos.length(); i++) {
extCompsBuildInfo.put(infos.getJSONObject(i));
}
}
}
} catch (Exception e) {
e.printStackTrace();
......@@ -1592,6 +1608,6 @@ public final class Compiler {
private String getExtCompDirPath(String type) {
createDir(project.getAssetsDirectory());
return project.getAssetsDirectory().getAbsolutePath() + SLASH +
EXT_COMPS_DIR_NAME + SLASH + type;
EXT_COMPS_DIR_NAME + SLASH + type.substring(0, type.lastIndexOf('.'));
}
}
......@@ -314,10 +314,23 @@ public final class ProjectBuilder {
}
File extCompJsonFile = new File (extCompDir, "component.json");
JSONObject extCompJson = new JSONObject(Resources.toString(
extCompJsonFile.toURI().toURL(), Charsets.UTF_8));
nameTypeMap.put(extCompJson.getString("name"),
extCompJson.getString("type"));
if (extCompJsonFile.exists()) {
JSONObject extCompJson = new JSONObject(Resources.toString(
extCompJsonFile.toURI().toURL(), Charsets.UTF_8));
nameTypeMap.put(extCompJson.getString("name"),
extCompJson.getString("type"));
} else { // multi-extension package
extCompJsonFile = new File(extCompDir, "components.json");
if (extCompJsonFile.exists()) {
JSONArray extCompJson = new JSONArray(Resources.toString(
extCompJsonFile.toURI().toURL(), Charsets.UTF_8));
for (int i = 0; i < extCompJson.length(); i++) {
JSONObject extCompDescriptor = extCompJson.getJSONObject(i);
nameTypeMap.put(extCompDescriptor.getString("name"),
extCompDescriptor.getString("type"));
}
}
}
}
return nameTypeMap;
......
......@@ -274,7 +274,8 @@
location="${class.dir}/ComponentProcessingLib" />
<mkdir dir="${ComponentProcessingLib-class.dir}" />
<ai.javac5 destdir="${ComponentProcessingLib-class.dir}">
<ai.javac destdir="${ComponentProcessingLib-class.dir}"
source="1.7" target="1.7">
<include name="${components.pkg}/scripts/ComponentProcessor.java" />
<include name="${components.pkg}/annotations/*.java" />
<include name="${components.pkg}/annotations/androidmanifest/*.java" />
......@@ -282,7 +283,7 @@
<pathelement location="${public.build.dir}/CommonConstants.jar" />
<pathelement location="${lib.dir}/guava/guava-14.0.1.jar" />
</classpath>
</ai.javac5>
</ai.javac>
<jar basedir="${ComponentProcessingLib-class.dir}"
destfile="${local.build.dir}/ComponentProcessingLib.jar" />
......@@ -302,7 +303,8 @@
location="${class.dir}/AnnotationProcessors" />
<mkdir dir="${AnnotationProcessors-class.dir}" />
<ai.javac5 destdir="${AnnotationProcessors-class.dir}">
<ai.javac destdir="${AnnotationProcessors-class.dir}"
source="1.7" target="1.7">
<include name="${components.pkg}/scripts/*.java" />
<exclude name="${components.pkg}/scripts/ExternalComponentGenerator.java" />
<classpath>
......@@ -312,7 +314,7 @@
<pathelement location="${build.dir}/common/CommonUtils.jar" />
<pathelement location="${lib.dir}/guava/guava-14.0.1.jar" />
</classpath>
</ai.javac5>
</ai.javac>
<copy todir="${AnnotationProcessors-class.dir}">
<fileset dir="${src.dir}">
......
......@@ -35,6 +35,7 @@ import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -745,17 +746,33 @@ public abstract class ComponentProcessor extends AbstractProcessor {
messager = processingEnv.getMessager();
List<Element> elements = new ArrayList<>();
List<Element> excludedElements = new ArrayList<>();
for (TypeElement te : annotations) {
if (te.getSimpleName().toString().equals("DesignerComponent")
|| te.getSimpleName().toString().equals("SimpleObject")) {
if (te.getSimpleName().toString().equals("DesignerComponent")) {
elements.addAll(roundEnv.getElementsAnnotatedWith(te));
} else if (te.getSimpleName().toString().equals("SimpleObject")) {
for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
processComponent(element);
SimpleObject annotation = element.getAnnotation(SimpleObject.class);
if (!annotation.external()) {
elements.add(element);
} else {
excludedElements.add(element);
}
}
}
}
elements.removeAll(excludedElements);
System.out.println("Number of elements = " + elements.size());
for (Element element : elements) {
processComponent(element);
}
// Put the component class names (including abstract classes)
componentTypes.addAll(components.keySet());
for (Element element : excludedElements) {
componentTypes.add(element.asType().toString()); // allow extensions to reference one another
}
// Remove non-components before calling outputResults.
List<String> removeList = Lists.newArrayList();
......@@ -1402,7 +1419,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
}
// {float, double, int, short, long} -> number
if (type.equals("float") || type.equals("double") || type.equals("int") ||
type.equals("short") || type.equals("long")) {
type.equals("short") || type.equals("long") || type.equals("byte") ||
type.equals("short")) {
return "number";
}
// YailList -> list
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment