From d724cbd9191cc2cffedaf7b5d5b90b7d4b43c2c3 Mon Sep 17 00:00:00 2001 From: Vineet Reynolds Date: Sun, 11 Aug 2013 13:23:09 +0530 Subject: [PATCH] FORGEPLUGINS-125 Added enhanced support for datetime types Temporal types are now detected and canonicalized. They are now removed as search criteria until they are better supported. Additionally, the AngularJS date filter is used to display dates and times in a better format in search results. --- .../angularjs/InspectionResultProcessor.java | 173 +++++++++++------- .../views/includes/searchFormInput.html.ftl | 4 +- .../views/includes/searchResults.html.ftl | 16 +- 3 files changed, 125 insertions(+), 68 deletions(-) diff --git a/src/main/java/org/jboss/forge/scaffold/angularjs/InspectionResultProcessor.java b/src/main/java/org/jboss/forge/scaffold/angularjs/InspectionResultProcessor.java index 57bffc8..6d91e56 100644 --- a/src/main/java/org/jboss/forge/scaffold/angularjs/InspectionResultProcessor.java +++ b/src/main/java/org/jboss/forge/scaffold/angularjs/InspectionResultProcessor.java @@ -7,9 +7,14 @@ package org.jboss.forge.scaffold.angularjs; import static org.jboss.forge.scaffoldx.metawidget.inspector.ForgeInspectionResultConstants.PRIMARY_KEY; +import static org.metawidget.inspector.InspectionResultConstants.DATETIME_TYPE; +import static org.metawidget.inspector.InspectionResultConstants.LABEL; +import static org.metawidget.inspector.InspectionResultConstants.NAME; +import static org.metawidget.inspector.InspectionResultConstants.TYPE; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; @@ -29,10 +34,10 @@ * An 'Inspection Result Processor' that enhances the inspection results provided by Metawidget. This class does not implement * the {@link org.metawidget.inspectionresultprocessor.iface.InspectionResultProcessor} of Metawidget since it needs access to * the injected Forge {@link ShellPrompt} instance. - * + * * This processor enhances the inspection results with HTML form labels for the inspected properties. It canonicalizes all * numerical types to the HTML5 'number' form input type. - * + * * It also prompts the user to choose a field to be displayed in the HTML form select fields. Form select fields may display * Ids, but this may not be intuitive, especially when other properties would be better suited visually. */ @@ -53,6 +58,7 @@ public List> enhanceResults(JavaClass entity, List propertyAttributes : inspectionResults) { populateLabelStrings(propertyAttributes); canonicalizeNumberTypes(propertyAttributes); + canonicalizeTemporalTypes(propertyAttributes); chooseRelationshipOptionLabels(entity, propertyAttributes); } return inspectionResults; @@ -60,38 +66,51 @@ public List> enhanceResults(JavaClass entity, List> inspectionResults) { - for(Map inspectionResult: inspectionResults){ + for (Map inspectionResult : inspectionResults) { boolean isPrimaryKey = Boolean.parseBoolean(inspectionResult.get(ForgeInspectionResultConstants.PRIMARY_KEY)); - if(isPrimaryKey){ - return inspectionResult.get("name"); + if (isPrimaryKey) { + return inspectionResult.get(NAME); } } throw new IllegalStateException("No Id was found for the class:" + entity.getName()); } private void populateLabelStrings(Map propertyAttributes) { - String propertyName = propertyAttributes.get("name"); - propertyAttributes.put("label", StringUtils.uncamelCase(propertyName)); + String propertyName = propertyAttributes.get(NAME); + propertyAttributes.put(LABEL, StringUtils.uncamelCase(propertyName)); } private void canonicalizeNumberTypes(Map propertyAttributes) { // Canonicalize all numerical types in Java to "number" for HTML5 form input type support - String propertyType = propertyAttributes.get("type"); + String propertyType = propertyAttributes.get(TYPE); if (propertyType.equals(short.class.getName()) || propertyType.equals(int.class.getName()) || propertyType.equals(long.class.getName()) || propertyType.equals(float.class.getName()) || propertyType.equals(double.class.getName()) || propertyType.equals(Short.class.getName()) || propertyType.equals(Integer.class.getName()) || propertyType.equals(Long.class.getName()) || propertyType.equals(Float.class.getName()) || propertyType.equals(Double.class.getName())) { - propertyAttributes.put("type", "number"); + propertyAttributes.put(TYPE, "number"); + } + } + + private void canonicalizeTemporalTypes(Map propertyAttributes) { + // Canonicalize all temporal types in Java to "temporal" for easier handling of date/time properties + String type = propertyAttributes.get(TYPE); + String datetimeType = propertyAttributes.get(DATETIME_TYPE); + if (type.equals(Date.class.getName()) && datetimeType == null) { + propertyAttributes.put("temporal", "true"); + propertyAttributes.put(DATETIME_TYPE, "both"); + } + if (datetimeType != null && (datetimeType.equals("date") || datetimeType.equals("time") || datetimeType.equals("both"))) { + propertyAttributes.put("temporal", "true"); } } - + private void chooseRelationshipOptionLabels(JavaClass entity, Map propertyAttributes) { // Extract simple type name of the relationship types boolean isManyToOneRel = Boolean.parseBoolean(propertyAttributes.get("many-to-one")); @@ -99,7 +118,7 @@ private void chooseRelationshipOptionLabels(JavaClass entity, Map fieldsToDisplay = getFieldsToDisplay(rightHandSideType); - String defaultField = fieldsToDisplay.size() > 0 ? fieldsToDisplay.get(0) : null; - propertyAttributes.put("optionLabel", prompt.promptChoiceTyped("Which property of " + rightHandSideSimpleName - + " do you want to display in the " + entity.getName() + " views ?", fieldsToDisplay, defaultField)); + JavaClass javaClass = getJavaClass(rightHandSideType); + List> rhsInspectionResults = metawidgetInspectorFacade.inspect(javaClass); + List fieldsToDisplay = getPropertiesToDisplay(getDisplayableProperties(rhsInspectionResults)); + InspectedProperty defaultField = fieldsToDisplay.size() > 0 ? fieldsToDisplay.get(0) : null; + InspectedProperty fieldToDisplay = prompt.promptChoiceTyped("Which property of " + rightHandSideSimpleName + + " do you want to display in the " + entity.getName() + " views ?", fieldsToDisplay, defaultField); + propertyAttributes.put("optionLabel", fieldToDisplay.getName()); + propertyAttributes.put("option-label-temporal-type", fieldToDisplay.getTemporalType()); + } + } + + private List getPropertiesToDisplay(List> displayableProperties) { + List fieldsToDisplay = new ArrayList(); + for (Map displayableProperty : displayableProperties) { + fieldsToDisplay.add(new InspectedProperty(displayableProperty)); + } + return fieldsToDisplay; + } + + private class InspectedProperty { + + private Map delegate; + + InspectedProperty(Map displayableProperty) { + this.delegate = displayableProperty; + } + + public String getTemporalType() { + return delegate.get(DATETIME_TYPE); + } + + public String getName() { + return delegate.get(NAME); + } + + @Override + public String toString() { + return delegate.get(NAME); } } // TODO; Extract this method into it's own class, for unit testing. - private List getFieldsToDisplay(String rightHandSideType) - { - List displayableProperties = new ArrayList(); - JavaClass javaClass = getJavaClass(rightHandSideType); - List> inspectionResults = metawidgetInspectorFacade.inspect(javaClass); - for (Map propertyAttributes : inspectionResults) { - boolean isManyToOneRel = Boolean.parseBoolean(propertyAttributes.get("many-to-one")); - boolean isOneToOneRel = Boolean.parseBoolean(propertyAttributes.get("one-to-one")); - boolean isNToManyRel = Boolean.parseBoolean(propertyAttributes.get("n-to-many")); - if (!isManyToOneRel && !isNToManyRel && !isOneToOneRel) - { - // Display only basic properties - String hidden = propertyAttributes.get("hidden"); - String required = propertyAttributes.get("required"); - boolean isHidden = Boolean.parseBoolean(hidden); - boolean isRequired = Boolean.parseBoolean(required); - if (!isHidden) - { - displayableProperties.add(propertyAttributes.get("name")); - } - else if (isRequired) - { - // Do nothing if hidden, unless required - displayableProperties.add(propertyAttributes.get("name")); - } - } - } + private List> getDisplayableProperties(List> inspectionResults) { + List> displayableProperties = new ArrayList>(); + for (Map propertyAttributes : inspectionResults) { + canonicalizeNumberTypes(propertyAttributes); + canonicalizeTemporalTypes(propertyAttributes); + boolean isManyToOneRel = Boolean.parseBoolean(propertyAttributes.get("many-to-one")); + boolean isOneToOneRel = Boolean.parseBoolean(propertyAttributes.get("one-to-one")); + boolean isNToManyRel = Boolean.parseBoolean(propertyAttributes.get("n-to-many")); + if (!isManyToOneRel && !isNToManyRel && !isOneToOneRel) { + // Display only basic properties. + String hidden = propertyAttributes.get("hidden"); + String required = propertyAttributes.get("required"); + boolean isHidden = Boolean.parseBoolean(hidden); + boolean isRequired = Boolean.parseBoolean(required); + if (!isHidden) { + displayableProperties.add(propertyAttributes); + } else if (isRequired) { + // Do nothing if hidden, unless required + displayableProperties.add(propertyAttributes); + } + } + } // If no properties were found suitable for display, add the primary key instead if (displayableProperties.size() < 1) { for (Map propertyAttributes : inspectionResults) { if (propertyAttributes.get(PRIMARY_KEY) != null) { - displayableProperties.add(propertyAttributes.get("name")); + displayableProperties.add(propertyAttributes); } } } return displayableProperties; } - - private String getSimpleName(String rightHandSideType) - { - return getJavaClass(rightHandSideType).getName(); + + private String getSimpleName(String rightHandSideType) { + return getJavaClass(rightHandSideType).getName(); } - - private JavaClass getJavaClass(String qualifiedType) - { - JavaSourceFacet java = this.project.getFacet(JavaSourceFacet.class); - try - { - JavaResource resource = java.getJavaResource(qualifiedType); - JavaClass javaClass = (JavaClass) resource.getJavaSource(); - return javaClass; - } - catch (FileNotFoundException fileEx) - { - throw new RuntimeException(fileEx); - } + + private JavaClass getJavaClass(String qualifiedType) { + JavaSourceFacet java = this.project.getFacet(JavaSourceFacet.class); + try { + JavaResource resource = java.getJavaResource(qualifiedType); + JavaClass javaClass = (JavaClass) resource.getJavaSource(); + return javaClass; + } catch (FileNotFoundException fileEx) { + throw new RuntimeException(fileEx); + } } } diff --git a/src/main/resources/scaffold/views/includes/searchFormInput.html.ftl b/src/main/resources/scaffold/views/includes/searchFormInput.html.ftl index b568d5d..122ca16 100644 --- a/src/main/resources/scaffold/views/includes/searchFormInput.html.ftl +++ b/src/main/resources/scaffold/views/includes/searchFormInput.html.ftl @@ -1,6 +1,6 @@ <#assign propertyLabel = "${property.label}"> -<#-- Display only non-hidden singular properties as search criteria. Omit collections --> -<#if (property.hidden!"false") != "true" && (property["n-to-many"]!"false") != "true"> +<#-- Display only non-hidden singular properties as search criteria. Omit collections and date/time properties --> +<#if (property.hidden!"false") != "true" && (property["n-to-many"]!"false") != "true" && (property["temporal"]!"false") != "true">
diff --git a/src/main/resources/scaffold/views/includes/searchResults.html.ftl b/src/main/resources/scaffold/views/includes/searchResults.html.ftl index c6c8ccf..87bb18e 100644 --- a/src/main/resources/scaffold/views/includes/searchResults.html.ftl +++ b/src/main/resources/scaffold/views/includes/searchResults.html.ftl @@ -16,9 +16,23 @@ <#list properties as property> <#if (property.hidden!"false") != "true"> <#if (property["many-to-one"]!"false") == "true" || (property["one-to-one"]!"false") == "true"> - {{result.${property.name}.${property.optionLabel}}} + <#if (property["option-label-temporal-type"]!"") == "date"> + {{result.${property.name}.${property.optionLabel}| date:'mediumDate'}} + <#elseif (property["option-label-temporal-type"]!"") == "time"> + {{result.${property.name}.${property.optionLabel}| date:'mediumTime'}} + <#elseif (property["option-label-temporal-type"]!"") == "both"> + {{result.${property.name}.${property.optionLabel}| date:'medium'}} + <#else> + {{result.${property.name}.${property.optionLabel}}} + <#elseif (property["n-to-many"]!"false") == "true"> <#-- Do nothing. We won't allow for display of collection properties in search results for now. --> + <#elseif (property["datetime-type"]!"") == "date"> + {{result.${property.name}| date:'mediumDate'}} + <#elseif (property["datetime-type"]!"") == "time"> + {{result.${property.name}| date:'mediumTime'}} + <#elseif (property["datetime-type"]!"") == "both"> + {{result.${property.name}| date:'medium'}} <#else> {{result.${property.name}}}