Commit 04738ccc authored by Evan W. Patton's avatar Evan W. Patton Committed by Susan Rati Lane

Implement versionName and dateBuilt annotation fields

This change adds two new fields toe @DesignerComponent. The first is a
versionName field so that extensions can display custom version names
rather than the version number. This can be used to mark an extension
build as beta or a release candidate, for example. The second change
is a dateBuilt field that is populated by ComponentProcessor at
compile time. This is useful for knowing when an extension is
compiled. Both pieces of information appear in the component help
widget, and version name is preferred over version if provided.

Change-Id: I12c97c9c0a3b18abd64a539f1c5072b975d47d14
parent 63738ecb
......@@ -1043,6 +1043,10 @@ public interface OdeMessages extends Messages {
@Description("Header for extension version information")
String externalComponentVersion();
@DefaultMessage("Date Built:")
@Description("Header to indicate the date an extension was compiled")
String dateBuilt();
@DefaultMessage("More information")
@Description("Label of the link to a component's reference docs")
String moreInformation();
......
......@@ -133,6 +133,26 @@ class ComponentDatabase implements ComponentDatabaseInterface {
return component.getVersion();
}
@Override
public String getComponentVersionName(String componentName) {
ComponentDefinition component = components.get(componentName);
if (component == null) {
throw new ComponentNotFoundException(componentName);
}
return component.getVersionName();
}
@Override
public String getComponentBuildDate(String componentName) {
ComponentDefinition component = components.get(componentName);
if (component == null) {
throw new ComponentNotFoundException(componentName);
}
return component.getDateBuilt();
}
@Override
public String getComponentType(String componentName){
ComponentDefinition component = components.get(componentName);
......@@ -309,6 +329,8 @@ class ComponentDatabase implements ComponentDatabaseInterface {
}
ComponentDefinition component = new ComponentDefinition(name,
Integer.parseInt(properties.get("version").asString().getString()),
optString(properties.get("versionName"), ""),
optString(properties.get("dateBuilt"), ""),
properties.get("type").asString().getString(),
Boolean.valueOf(properties.get("external").asString().getString()),
properties.get("categoryString").asString().getString(),
......@@ -325,6 +347,20 @@ class ComponentDatabase implements ComponentDatabaseInterface {
return true;
}
/**
* Extracts a string from the given value. If value is null, returns the defaultValue.
* @param value JSON value to process
* @param defaultValue Alternative value if {@code value} is not valid
* @return A non-null String containing either the String version of {@code value} or
* {@code defaultValue}
*/
private String optString(JSONValue value, String defaultValue) {
if (value == null) {
return defaultValue;
}
return value.asString().getString();
}
/*
* Enters property information into the component descriptor.
*/
......
......@@ -86,11 +86,22 @@ public final class ComponentHelpWidget extends AbstractPaletteItemWidget {
? referenceComponentsUrl + "index.html"
: referenceComponentsUrl + categoryDocUrlString + ".html#" + scd.getName();
}
if (version > 0) {
if (!scd.getVersionName().equals("")) {
HTML html = new HTML("<b>" + MESSAGES.externalComponentVersion() + "</b> " +
scd.getVersionName());
html.setStyleName("ode-ComponentHelpPopup-Body");
inner.add(html);
} else if (version > 0) {
HTML html = new HTML("<b>" + MESSAGES.externalComponentVersion() + "</b> " + version);
html.setStyleName("ode-ComponentHelpPopup-Body");
inner.add(html);
}
if (scd.getExternal() && scd.getDateBuilt() != null && !scd.getDateBuilt().equals("")) {
String date = scd.getDateBuilt().split("T")[0];
HTML dateCreatedHtml = new HTML("<b>" + MESSAGES.dateBuilt() + "</b> <time datetime=\"" + scd.getDateBuilt() + "\">" + date + "</time>");
dateCreatedHtml.setStyleName("ode-ComponentHelpPopup-Body");
inner.add(dateCreatedHtml);
}
if (url != null) { // only show if there is a relevant URL
HTML link = new HTML("<a href=\"" + url + "\" target=\"_blank\">" +
MESSAGES.moreInformation() + "</a>");
......
......@@ -95,7 +95,11 @@ public final class SimpleComponentDescriptor {
private MockComponent cachedMockComponent = null;
// The version of the extension (meaning is defined by the extension author).
private int version = -1;
private final int version;
private final String versionName;
private final String dateBuilt;
// Component database: information about components (including their properties and events)
private final SimpleComponentDatabase COMPONENT_DATABASE;
......@@ -170,6 +174,8 @@ public final class SimpleComponentDescriptor {
public SimpleComponentDescriptor(String name,
SimpleEditor editor,
int version,
String versionName,
String dateBuilt,
String helpString,
String helpUrl,
String categoryDocUrlString,
......@@ -179,6 +185,8 @@ public final class SimpleComponentDescriptor {
this.name = name;
this.editor = editor;
this.version = version;
this.versionName = versionName;
this.dateBuilt = dateBuilt;
this.helpString = helpString;
this.helpUrl = helpUrl;
this.categoryDocUrlString = categoryDocUrlString;
......@@ -283,6 +291,24 @@ public final class SimpleComponentDescriptor {
return version;
}
/**
* Returns the custom version name of the component, if any.
*
* @return component version name
*/
public String getVersionName() {
return versionName;
}
/**
* Returns the date the component was built, if any.
*
* @return ISO 8601 formated date the component was built
*/
public String getDateBuilt() {
return dateBuilt;
}
/**
* Returns a draggable image for the component. Used when dragging a
* component from the palette onto the form.
......
......@@ -149,6 +149,8 @@ public class YoungAndroidPalettePanel extends Composite implements SimplePalette
removeComponent(componentTypeName);
}
int version = COMPONENT_DATABASE.getComponentVersion(componentTypeName);
String versionName = COMPONENT_DATABASE.getComponentVersionName(componentTypeName);
String dateBuilt = COMPONENT_DATABASE.getComponentBuildDate(componentTypeName);
String helpString = COMPONENT_DATABASE.getHelpString(componentTypeName);
String helpUrl = COMPONENT_DATABASE.getHelpUrl(componentTypeName);
String categoryDocUrlString = COMPONENT_DATABASE.getCategoryDocUrlString(componentTypeName);
......@@ -159,7 +161,7 @@ public class YoungAndroidPalettePanel extends Composite implements SimplePalette
ComponentCategory category = ComponentCategory.valueOf(categoryString);
if (showOnPalette && showCategory(category)) {
SimplePaletteItem item = new SimplePaletteItem(
new SimpleComponentDescriptor(componentTypeName, editor, version, helpString, helpUrl,
new SimpleComponentDescriptor(componentTypeName, editor, version, versionName, dateBuilt, helpString, helpUrl,
categoryDocUrlString, showOnPalette, nonVisible, external),
dropTargetProvider);
simplePaletteItems.put(componentTypeName, item);
......
......@@ -27,6 +27,8 @@ public interface ComponentDatabaseInterface {
public static class ComponentDefinition {
private final String name;
private final int version;
private final String versionName;
private final String dateBuilt;
private final String type;
private final boolean external;
private final String categoryString;
......@@ -43,11 +45,13 @@ public interface ComponentDatabaseInterface {
private final String iconName;
private final String typeDescription;
public ComponentDefinition(String name, int version, String type, boolean external,
public ComponentDefinition(String name, int version, String versionName, String dateBuilt, String type, boolean external,
String categoryString, String helpString, String helpUrl,
boolean showOnPalette, boolean nonVisible, String iconName, String typeDescription) {
this.name = name;
this.version = version;
this.versionName = versionName;
this.dateBuilt = dateBuilt;
this.type = type;
this.external = external;
this.categoryString = categoryString;
......@@ -90,6 +94,14 @@ public interface ComponentDatabaseInterface {
return version;
}
public String getVersionName() {
return versionName;
}
public String getDateBuilt() {
return dateBuilt;
}
public String getType() {
return type;
}
......@@ -360,6 +372,22 @@ public interface ComponentDatabaseInterface {
*/
int getComponentVersion(String componentName);
/**
* Returns the version name of a component.
*
* @param componentName name of the component to query
* @return the component version name, or the empty string if none is provided
*/
String getComponentVersionName(String componentName);
/**
* Returns the build date of a component.
*
* @param componentName mame of the component to query
* @return the component build date, or the empty string if non is provided
*/
String getComponentBuildDate(String componentName);
/**
* Returns the String version of a component's category. Note that this
* is the result of calling getString() on a ComponentCategory, not the
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2017 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -84,4 +84,23 @@ public @interface DesignerComponent {
* the global App Inventor minimum SDK unless otherwise specified.
*/
int androidMinSdk() default ComponentConstants.APP_INVENTOR_MIN_SDK;
/**
* A custom version name for the component version. If provided, it
* will be shown in the component help popup in place of the
* {@link #version()}. This can be useful for marking beta or release
* candidate versions of extensions, for example.
* @return The custom version name, if any.
*/
String versionName() default "";
/**
* A ISO 8601 datetime string that indicates when the component was
* built. This information will be shown in the component help popup
* for extensions. This is automatically populated by
* {@link com.google.appinventor.components.scripts.ExternalComponentGenerator}.
* @return An ISO 8601 string containing the compilation time of
* the component.
*/
String dateBuilt() default "";
}
......@@ -80,6 +80,12 @@ public final class ComponentDescriptorGenerator extends ComponentProcessor {
sb.append(Boolean.toString(component.external));
sb.append("\",\n \"version\": \"");
sb.append(component.getVersion());
if (component.getVersionName() != null && !component.getVersionName().equals("")) {
sb.append("\",\n \"versionName\": \"");
sb.append(component.getVersionName());
}
sb.append("\",\n \"dateBuilt\": \"");
sb.append(component.getDateBuilt());
sb.append("\",\n \"categoryString\": \"");
sb.append(component.getCategoryString());
sb.append("\",\n \"helpString\": ");
......
......@@ -35,7 +35,9 @@ import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
......@@ -48,6 +50,8 @@ import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
......@@ -58,7 +62,6 @@ import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.AbstractTypeVisitor7;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor7;
import javax.lang.model.util.Types;
......@@ -576,6 +579,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
private boolean nonVisible;
private String iconName;
private int androidMinSdk;
private String versionName;
private String dateBuilt;
protected ComponentInfo(Element element) {
super(element.getSimpleName().toString(), // Short name
......@@ -596,6 +601,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
events = Maps.newTreeMap();
abstractClass = element.getModifiers().contains(Modifier.ABSTRACT);
external = false;
versionName = null;
dateBuilt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(new Date());
for (AnnotationMirror am : element.getAnnotationMirrors()) {
DeclaredType dt = am.getAnnotationType();
String annotationName = am.getAnnotationType().toString();
......@@ -608,6 +615,27 @@ public abstract class ComponentProcessor extends AbstractProcessor {
designerComponent = true;
DesignerComponent designerComponentAnnotation =
element.getAnnotation(DesignerComponent.class);
Map values = elementUtils.getElementValuesWithDefaults(am);
for (Map.Entry entry : (Set<Map.Entry>) values.entrySet()) {
if (((ExecutableElement) entry.getKey()).getSimpleName().contentEquals("dateBuilt")) {
entry.setValue(new AnnotationValue() {
@Override
public Object getValue() {
return dateBuilt;
}
@Override
public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
return v.visit(this, p);
}
@Override
public String toString() {
return "\"" + dateBuilt + "\"";
}
});
}
}
// Override javadoc description with explicit description
// if provided.
......@@ -634,6 +662,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
nonVisible = designerComponentAnnotation.nonVisible();
iconName = designerComponentAnnotation.iconName();
androidMinSdk = designerComponentAnnotation.androidMinSdk();
versionName = designerComponentAnnotation.versionName();
}
}
}
......@@ -743,6 +772,14 @@ public abstract class ComponentProcessor extends AbstractProcessor {
return androidMinSdk;
}
protected String getVersionName() {
return versionName;
}
protected String getDateBuilt() {
return dateBuilt;
}
private String getDisplayNameForComponentType(String componentTypeName) {
// Users don't know what a 'Form' is. They know it as a 'Screen'.
return "Form".equals(componentTypeName) ? "Screen" : componentTypeName;
......
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