AI2 Component  (Version nb184)
ComponentDescriptorGenerator.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2019 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.scripts;
8 
9 import com.google.appinventor.common.utils.StringUtils;
11 
12 import java.io.IOException;
13 import java.io.Writer;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 
20 import javax.tools.Diagnostic;
21 import javax.tools.FileObject;
22 
82  // Where to write results.
83  private static final String OUTPUT_FILE_NAME = "simple_components.json";
84 
85  private void outputComponent(ComponentInfo component, StringBuilder sb) {
86  sb.append("{ \"type\": \"");
87  sb.append(component.type);
88  sb.append("\",\n \"name\": \"");
89  sb.append(component.name);
90  sb.append("\",\n \"external\": \"");
91  sb.append(Boolean.toString(component.external));
92  sb.append("\",\n \"version\": \"");
93  sb.append(component.getVersion());
94  if (component.getVersionName() != null && !component.getVersionName().equals("")) {
95  sb.append("\",\n \"versionName\": \"");
96  sb.append(component.getVersionName());
97  }
98  sb.append("\",\n \"dateBuilt\": \"");
99  sb.append(component.getDateBuilt());
100  sb.append("\",\n \"categoryString\": \"");
101  sb.append(component.getCategoryString());
102  sb.append("\",\n \"helpString\": ");
103  sb.append(formatDescription(component.getHelpDescription()));
104  sb.append(",\n \"helpUrl\": ");
105  sb.append(formatDescription(component.getHelpUrl()));
106  sb.append(",\n \"showOnPalette\": \"");
107  sb.append(component.getShowOnPalette());
108  sb.append("\",\n \"nonVisible\": \"");
109  sb.append(component.getNonVisible());
110  sb.append("\",\n \"iconName\": \"");
111  sb.append(component.getIconName());
112  sb.append("\",\n \"androidMinSdk\": ");
113  sb.append(component.getAndroidMinSdk());
114  outputConditionalAnnotations(component, sb);
115  sb.append(",\n \"properties\": [");
116  String separator = "";
117  Set<String> alwaysSendProperties = new HashSet<String>();
118  Map<String, String> defaultValues = new HashMap<String, String>();
119  for (Map.Entry<String, DesignerProperty> entry : component.designerProperties.entrySet()) {
120  String propertyName = entry.getKey();
121  DesignerProperty dp = entry.getValue();
122  sb.append(separator);
123  if (dp.alwaysSend()) {
124  alwaysSendProperties.add(propertyName);
125  // We need to include the default value since it will be sent if no
126  // value is specified (we don't write it in the .scm file).
127  defaultValues.put(propertyName, dp.defaultValue());
128  }
129  outputProperty(propertyName, dp, sb);
130  separator = ",\n";
131  }
132  // We need additional information about properties in the blocks editor,
133  // and we need all of them, not just the Designer properties. We output
134  // the entire set separately for use by the blocks editor to keep things simple.
135  sb.append("],\n \"blockProperties\": [");
136  separator = "";
137  for (Property prop : component.properties.values()) {
138  sb.append(separator);
139  // Output properties that are not user-visible, but mark them as invisible
140  // Note: carrying this over from the old Java blocks editor. I'm not sure
141  // that we'll actually do anything with invisible properties in the blocks
142  // editor. (sharon@google.com)
143  outputBlockProperty(prop.name, prop, alwaysSendProperties.contains(prop.name), defaultValues.get(prop.name), sb);
144  separator = ",\n ";
145  }
146  sb.append("],\n \"events\": [");
147  separator = "";
148  for (Event event : component.events.values()) {
149  sb.append(separator);
150  outputBlockEvent(event.name, event, sb, event.userVisible, event.deprecated);
151  separator = ",\n ";
152  }
153  sb.append("],\n \"methods\": [");
154  separator = "";
155  for (Method method : component.methods.values()) {
156  sb.append(separator);
157  outputBlockMethod(method.name, method, sb, method.userVisible, method.deprecated);
158  separator = ",\n ";
159  }
160  sb.append("]");
161  // Output assets for extensions (consumed by ExternalComponentGenerator and buildserver)
162  if (component.external && component.assets.size() > 0) {
163  sb.append(",\n \"assets\": [");
164  for (String asset : component.assets) {
165  sb.append("\"");
166  sb.append(asset.replaceAll("\\\\", "\\\\").replaceAll("\"", "\\\""));
167  sb.append("\",");
168  }
169  sb.setLength(sb.length() - 1);
170  sb.append("]");
171  }
172  sb.append("}\n");
173  }
174 
184  private static void outputMultimap(StringBuilder sb, String indent, Map<String, String[]> map) {
185  boolean first = true;
186  sb.append("{");
187  for (Map.Entry<String, String[]> entry : map.entrySet()) {
188  if (!first) sb.append(",");
189  sb.append("\n");
190  sb.append(indent);
191  sb.append(" \"");
192  sb.append(entry.getKey());
193  sb.append("\": [\n");
194  sb.append(indent);
195  sb.append(" \"");
196  StringUtils.join(sb, "\",\n" + indent + " \"", entry.getValue());
197  sb.append("\"\n");
198  sb.append(indent);
199  sb.append(" ]");
200  first = false;
201  }
202  sb.append("\n");
203  sb.append(indent);
204  sb.append("}");
205  }
206 
214  private void outputConditionalAnnotations(ComponentInfo component, StringBuilder sb) {
215  if (component.conditionalPermissions.size() +
216  component.conditionalBroadcastReceivers.size() +
217  component.conditionalServices.size() +
218  component.conditionalContentProviders.size() == 0) {
219  return;
220  }
221  sb.append(",\n \"conditionals\":{\n ");
222  boolean first = true;
223  if (component.conditionalPermissions.size() > 0) {
224  sb.append("\"permissions\": ");
225  outputMultimap(sb, " ", component.conditionalPermissions);
226  first = false;
227  }
228  if (component.conditionalBroadcastReceivers.size() > 0) {
229  if (!first) sb.append(",\n ");
230  sb.append("\"broadcastReceivers\": ");
231  outputMultimap(sb, " ", component.conditionalBroadcastReceivers);
232  first = false;
233  }
234  if (component.conditionalServices.size() > 0) {
235  if (!first) sb.append(",\n ");
236  sb.append("\"services\": ");
237  outputMultimap(sb, " ", component.conditionalServices);
238  first = false;
239  }
240  if (component.conditionalContentProviders.size() > 0) {
241  if (!first) sb.append(",\n ");
242  sb.append("\"contentProviders\": ");
243  outputMultimap(sb, " ", component.conditionalContentProviders);
244  first = false;
245  }
246  // Add other annotations here as needed
247  sb.append("\n }");
248  }
249 
250  private void outputProperty(String propertyName, DesignerProperty dp, StringBuilder sb) {
251  sb.append("{ \"name\": \"");
252  sb.append(propertyName);
253  sb.append("\", \"editorType\": \"");
254  sb.append(dp.editorType());
255  sb.append("\", \"defaultValue\": \"");
256  sb.append(dp.defaultValue().replace("\"", "\\\""));
257 
258  sb.append("\", \"editorArgs\": ");
259  String[] editorArgs = dp.editorArgs();
260  for (int idx = 0; idx < editorArgs.length; idx += 1)
261  editorArgs[idx] = "\"" + editorArgs[idx].replace("\"", "\\\"") + "\"";
262 
263  StringBuilder listLiteralBuilder = new StringBuilder();
264  listLiteralBuilder.append("[");
265 
266  if (editorArgs.length > 0) {
267  listLiteralBuilder.append(editorArgs[0]);
268 
269  for (int ind = 1; ind < editorArgs.length; ind += 1) {
270  listLiteralBuilder.append(", ");
271  listLiteralBuilder.append(editorArgs[ind]);
272  }
273  }
274 
275  listLiteralBuilder.append("]");
276 
277  sb.append(listLiteralBuilder.toString());
278  if (dp.alwaysSend()) {
279  sb.append(", \"alwaysSend\": true");
280  }
281  sb.append("}");
282  }
283 
293  private void outputBlockProperty(String propertyName, Property prop, boolean alwaysSend, String defaultValue, StringBuilder sb) {
294  sb.append("{ \"name\": \"");
295  sb.append(propertyName);
296  sb.append("\", \"description\": ");
297  sb.append(formatDescription(prop.getDescription()));
298  sb.append(", \"type\": \"");
299  sb.append(javaTypeToYailType(prop.getType()));
300  sb.append("\", \"rw\": \"");
301  sb.append(prop.isUserVisible() ? prop.getRwString() : "invisible");
302  // [lyn, 2015/12/20] Added deprecated field to JSON.
303  // If we want to save space in simple-components.json,
304  // we could include this field only when it is "true"
305  sb.append("\", \"deprecated\": \"");
306  sb.append(prop.isDeprecated());
307  sb.append("\"");
308  if (alwaysSend) {
309  sb.append(", \"alwaysSend\": true, \"defaultValue\": \"");
310  sb.append(defaultValue.replaceAll("\"", "\\\""));
311  sb.append("\"");
312  }
313  sb.append("}");
314  }
315 
316  private void outputBlockEvent(String eventName, Event event, StringBuilder sb,
317  boolean userVisible, boolean deprecated) {
318  sb.append("{ \"name\": \"");
319  sb.append(eventName);
320  sb.append("\", \"description\": ");
321  sb.append(formatDescription(event.description));
322  // [lyn, 2015/12/20] Remove userVisible field from JSON, which is no longer used for events.
323  // sb.append(", \"userVisible\": \"" + userVisible + "\"");
324  // [lyn, 2015/12/20] Added deprecated field to JSON.
325  // If we want to save space in simple-components.json,
326  // we could include this field only when it is "true"
327  sb.append(", \"deprecated\": \"" + deprecated + "\"");
328  sb.append(", \"params\": ");
329  outputParameters(event.parameters, sb);
330  sb.append("}\n");
331  }
332 
333  private void outputBlockMethod(String methodName, Method method, StringBuilder sb,
334  boolean userVisible, boolean deprecated) {
335  sb.append("{ \"name\": \"");
336  sb.append(methodName);
337  sb.append("\", \"description\": ");
338  sb.append(formatDescription(method.description));
339  // [lyn, 2015/12/20] Remove userVisible field from JSON, which is no longer used for methods.
340  // sb.append(", \"userVisible\": \"" + userVisible + "\"");
341  // [lyn, 2015/12/20] Added deprecated field to JSON.
342  // If we want to save space in simple-components.json,
343  // we could include this field only when it is "true"
344  sb.append(", \"deprecated\": \"" + deprecated + "\"");
345  sb.append(", \"params\": ");
346  outputParameters(method.parameters, sb);
347  if (method.getReturnType() != null) {
348  sb.append(", \"returnType\": \"");
349  sb.append(javaTypeToYailType(method.getReturnType()));
350  sb.append("\"}");
351  } else {
352  sb.append("}");
353  }
354  }
355 
356  /*
357  * Output a parameter list (including surrounding [])
358  */
359  private void outputParameters(List<Parameter> params, StringBuilder sb) {
360  sb.append("[");
361  String separator = "";
362  for (Parameter p : params) {
363  sb.append(separator);
364  sb.append("{ \"name\": \"");
365  sb.append(p.name);
366  sb.append("\", \"type\": \"");
367  sb.append(javaTypeToYailType(p.type));
368  sb.append("\"}");
369  separator = ",";
370  }
371  sb.append("]");
372  }
373 
374  @Override
375  protected void outputResults() throws IOException {
376  StringBuilder sb = new StringBuilder();
377 
378  sb.append('[');
379  String separator = "";
380 
381  // Components are already sorted.
382  for (Map.Entry<String, ComponentInfo> entry : components.entrySet()) {
383  ComponentInfo component = entry.getValue();
384  sb.append(separator);
385  outputComponent(component, sb);
386  separator = ",\n";
387  }
388 
389  sb.append(']');
390 
391  FileObject src = createOutputFileObject(OUTPUT_FILE_NAME);
392  Writer writer = src.openWriter();
393  writer.write(sb.toString());
394  writer.flush();
395  writer.close();
396  messager.printMessage(Diagnostic.Kind.NOTE, "Wrote file " + src.toUri());
397  }
398 
399  /*
400  * Format a description string as a json string. Note that the returned value
401  * include surrounding double quotes.
402  */
403  private static String formatDescription(String description) {
404  return StringUtils.toJson(description);
405  }
406 }
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.methods
final SortedMap< String, Method > methods
Definition: ComponentProcessor.java:746
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.external
boolean external
Definition: ComponentProcessor.java:768
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.events
final SortedMap< String, Event > events
Definition: ComponentProcessor.java:751
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components
com.google.appinventor.components.scripts.ComponentProcessor.Method
Definition: ComponentProcessor.java:488
com.google.appinventor.components.scripts.ComponentProcessor.Parameter
Definition: ComponentProcessor.java:223
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.properties
final SortedMap< String, Property > properties
Definition: ComponentProcessor.java:741
com.google.appinventor.components.scripts.ComponentDescriptorGenerator
Definition: ComponentDescriptorGenerator.java:81
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.conditionalBroadcastReceivers
final Map< String, String[]> conditionalBroadcastReceivers
Definition: ComponentProcessor.java:662
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.getCategoryString
String getCategoryString()
Definition: ComponentProcessor.java:928
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.getHelpDescription
String getHelpDescription()
Definition: ComponentProcessor.java:898
com.google.appinventor.components.scripts.ComponentDescriptorGenerator.outputResults
void outputResults()
Definition: ComponentDescriptorGenerator.java:375
com.google.appinventor.components.scripts.ComponentProcessor.Event
Definition: ComponentProcessor.java:461
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.conditionalPermissions
final Map< String, String[]> conditionalPermissions
Definition: ComponentProcessor.java:656
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.type
final String type
Definition: ComponentProcessor.java:767
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.assets
final Set< String > assets
Definition: ComponentProcessor.java:689
com.google.appinventor.components.scripts.ComponentProcessor.messager
Messager messager
Definition: ComponentProcessor.java:194
com.google.appinventor.components.scripts.ComponentProcessor.ParameterizedFeature.parameters
final List< Parameter > parameters
Definition: ComponentProcessor.java:419
com.google.appinventor.components.scripts.ComponentProcessor.Method.getReturnType
String getReturnType()
Definition: ComponentProcessor.java:500
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.conditionalContentProviders
final Map< String, String[]> conditionalContentProviders
Definition: ComponentProcessor.java:674
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.getHelpUrl
String getHelpUrl()
Definition: ComponentProcessor.java:907
com.google.appinventor.components.annotations.DesignerProperty.editorType
String editorType() default PropertyTypeConstants.PROPERTY_TYPE_TEXT
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.getVersion
int getVersion()
Definition: ComponentProcessor.java:938
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo
Definition: ComponentProcessor.java:644
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.conditionalServices
final Map< String, String[]> conditionalServices
Definition: ComponentProcessor.java:668
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.getShowOnPalette
boolean getShowOnPalette()
Definition: ComponentProcessor.java:948
com.google.appinventor.components.scripts.ComponentProcessor.ComponentInfo.designerProperties
final SortedMap< String, DesignerProperty > designerProperties
Definition: ComponentProcessor.java:734
com.google.appinventor.components.scripts.ComponentProcessor.components
final SortedMap< String, ComponentInfo > components
Definition: ComponentProcessor.java:207
com.google.appinventor.components.annotations.DesignerProperty.defaultValue
String defaultValue() default ""
com.google.appinventor.components.annotations.DesignerProperty.alwaysSend
boolean alwaysSend() default false
com.google
com
com.google.appinventor.components.scripts.ComponentProcessor
Definition: ComponentProcessor.java:124
com.google.appinventor.components.annotations
com.google.appinventor.components.annotations.DesignerProperty.editorArgs
String[] editorArgs() default
Definition: DesignerProperty.java:55
com.google.appinventor