Commit 2013ab26 authored by Evan W. Patton's avatar Evan W. Patton Committed by Jeffrey Schiller

Enforce that block definitions exist for designer properties

If a designer property is included in a component without a
corresponding block entry, then the YAIL code generator will throw an
exception attempting to look up the type of the property when
generating set-and-coerce-property! code for the designer. This change
enforces that every @DesignerProperty have a corresponding
@SimpleProperty to ensure that components and extensions include the
necessary information.

Change-Id: I85cf53b1b657254658fd0cb0f05af3dde6b32740
parent 35bb8a21
...@@ -405,8 +405,12 @@ Blockly.Yail.getPropertySetterString = function(componentName, componentType, pr ...@@ -405,8 +405,12 @@ Blockly.Yail.getPropertySetterString = function(componentName, componentType, pr
var code = Blockly.Yail.YAIL_SET_AND_COERCE_PROPERTY + Blockly.Yail.YAIL_QUOTE + var code = Blockly.Yail.YAIL_SET_AND_COERCE_PROPERTY + Blockly.Yail.YAIL_QUOTE +
componentName + Blockly.Yail.YAIL_SPACER + Blockly.Yail.YAIL_QUOTE + propertyName + componentName + Blockly.Yail.YAIL_SPACER + Blockly.Yail.YAIL_QUOTE + propertyName +
Blockly.Yail.YAIL_SPACER; Blockly.Yail.YAIL_SPACER;
var propType = Blockly.Yail.YAIL_QUOTE + var propDef = componentDb.getPropertyForType(componentType, propertyName);
componentDb.getPropertyForType(componentType, propertyName).type; // If a designer property does not have a corresponding block property, then propDef will be
// undefined. In this case, we assume "any" as the type. A corresponding fix is included in
// ComponentProcessor to enforce that newer components/extensions always have both a designer
// and block definition.
var propType = Blockly.Yail.YAIL_QUOTE + (propDef ? propDef.type : "any");
var value = Blockly.Yail.getPropertyValueString(propertyValue, propType); var value = Blockly.Yail.getPropertyValueString(propertyValue, propType);
code = code.concat(value + Blockly.Yail.YAIL_SPACER + propType + Blockly.Yail.YAIL_CLOSE_BLOCK); code = code.concat(value + Blockly.Yail.YAIL_SPACER + propType + Blockly.Yail.YAIL_CLOSE_BLOCK);
return code; return code;
......
...@@ -316,6 +316,7 @@ public class GameClient extends AndroidNonvisibleComponent ...@@ -316,6 +316,7 @@ public class GameClient extends AndroidNonvisibleComponent
@DesignerProperty( @DesignerProperty(
editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
defaultValue = "http://appinvgameserver.appspot.com") defaultValue = "http://appinvgameserver.appspot.com")
@SimpleProperty(userVisible = false)
public void ServiceURL(String url){ public void ServiceURL(String url){
if (url.endsWith("/")) { if (url.endsWith("/")) {
this.serviceUrl = url.substring(0, url.length() - 1); this.serviceUrl = url.substring(0, url.length() - 1);
......
...@@ -36,6 +36,7 @@ import java.io.IOException; ...@@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -67,6 +68,7 @@ import java.lang.annotation.Annotation; ...@@ -67,6 +68,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject; import javax.tools.FileObject;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
...@@ -105,6 +107,9 @@ import javax.tools.StandardLocation; ...@@ -105,6 +107,9 @@ import javax.tools.StandardLocation;
public abstract class ComponentProcessor extends AbstractProcessor { public abstract class ComponentProcessor extends AbstractProcessor {
private static final String OUTPUT_PACKAGE = ""; private static final String OUTPUT_PACKAGE = "";
public static final String MISSING_SIMPLE_PROPERTY_ANNOTATION =
"Designer property %s does not have a corresponding @SimpleProperty annotation.";
// Returned by getSupportedAnnotationTypes() // Returned by getSupportedAnnotationTypes()
private static final Set<String> SUPPORTED_ANNOTATION_TYPES = ImmutableSet.of( private static final Set<String> SUPPORTED_ANNOTATION_TYPES = ImmutableSet.of(
"com.google.appinventor.components.annotations.DesignerComponent", "com.google.appinventor.components.annotations.DesignerComponent",
...@@ -1229,6 +1234,8 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -1229,6 +1234,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
Element componentElement) { Element componentElement) {
// We no longer support properties that use the variant type. // We no longer support properties that use the variant type.
Map<String, Element> propertyElementsToCheck = new HashMap<>();
for (Element element : componentElement.getEnclosedElements()) { for (Element element : componentElement.getEnclosedElements()) {
if (!isPublicMethod(element)) { if (!isPublicMethod(element)) {
continue; continue;
...@@ -1241,6 +1248,7 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -1241,6 +1248,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
DesignerProperty designerProperty = element.getAnnotation(DesignerProperty.class); DesignerProperty designerProperty = element.getAnnotation(DesignerProperty.class);
if (designerProperty != null) { if (designerProperty != null) {
componentInfo.designerProperties.put(propertyName, designerProperty); componentInfo.designerProperties.put(propertyName, designerProperty);
propertyElementsToCheck.put(propertyName, element);
} }
// If property is overridden without again using SimpleProperty, remove // If property is overridden without again using SimpleProperty, remove
...@@ -1310,6 +1318,20 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -1310,6 +1318,20 @@ public abstract class ComponentProcessor extends AbstractProcessor {
} }
} }
} }
// Verify that every DesignerComponent has a corresponding property entry. A mismatch results
// in App Inventor being unable to generate code for the designer since the type information
// is in the block property only. We check that the designer property name is also present
// in the block properties. If not, an error is reported and the build terminates.
Set<String> propertyNames = new HashSet<>(componentInfo.designerProperties.keySet());
propertyNames.removeAll(componentInfo.properties.keySet());
if (!propertyNames.isEmpty()) {
for (String propertyName : propertyNames) {
messager.printMessage(Kind.ERROR,
String.format(MISSING_SIMPLE_PROPERTY_ANNOTATION, propertyName),
propertyElementsToCheck.get(propertyName));
}
}
} }
// Note: The top halves of the bodies of processEvent() and processMethods() // Note: The top halves of the bodies of processEvent() and processMethods()
......
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