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
var code = Blockly.Yail.YAIL_SET_AND_COERCE_PROPERTY + Blockly.Yail.YAIL_QUOTE +
componentName + Blockly.Yail.YAIL_SPACER + Blockly.Yail.YAIL_QUOTE + propertyName +
Blockly.Yail.YAIL_SPACER;
var propType = Blockly.Yail.YAIL_QUOTE +
componentDb.getPropertyForType(componentType, propertyName).type;
var propDef = componentDb.getPropertyForType(componentType, propertyName);
// 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);
code = code.concat(value + Blockly.Yail.YAIL_SPACER + propType + Blockly.Yail.YAIL_CLOSE_BLOCK);
return code;
......
......@@ -316,6 +316,7 @@ public class GameClient extends AndroidNonvisibleComponent
@DesignerProperty(
editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
defaultValue = "http://appinvgameserver.appspot.com")
@SimpleProperty(userVisible = false)
public void ServiceURL(String url){
if (url.endsWith("/")) {
this.serviceUrl = url.substring(0, url.length() - 1);
......
......@@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
......@@ -67,6 +68,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
......@@ -105,6 +107,9 @@ import javax.tools.StandardLocation;
public abstract class ComponentProcessor extends AbstractProcessor {
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()
private static final Set<String> SUPPORTED_ANNOTATION_TYPES = ImmutableSet.of(
"com.google.appinventor.components.annotations.DesignerComponent",
......@@ -1229,6 +1234,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
Element componentElement) {
// We no longer support properties that use the variant type.
Map<String, Element> propertyElementsToCheck = new HashMap<>();
for (Element element : componentElement.getEnclosedElements()) {
if (!isPublicMethod(element)) {
continue;
......@@ -1241,6 +1248,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
DesignerProperty designerProperty = element.getAnnotation(DesignerProperty.class);
if (designerProperty != null) {
componentInfo.designerProperties.put(propertyName, designerProperty);
propertyElementsToCheck.put(propertyName, element);
}
// If property is overridden without again using SimpleProperty, remove
......@@ -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()
......
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