/*
* gvNIX is an open source tool for rapid application development (RAD).
* Copyright (C) 2010 Generalitat Valenciana
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gvnix.web.report.roo.addon.addon;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.gvnix.web.report.roo.addon.annotations.GvNIXReports;
import org.springframework.roo.addon.web.mvc.controller.annotations.scaffold.RooWebScaffold;
import org.springframework.roo.classpath.PhysicalTypeDetails;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue;
import org.springframework.roo.classpath.details.annotations.StringAttributeValue;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.logging.HandlerUtils;
/**
* Implementation of operations this add-on offers.
*
* @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a
* href="http://www.dgti.gva.es">General Directorate for Information
* Technologies (DGTI)</a>
* @since 0.6
*/
@Component
// Use these Apache Felix annotations to register your commands class in the Roo
// container
@Service
public class ReportOperationsImpl implements ReportOperations {
private static final Logger logger = HandlerUtils
.getLogger(ReportOperationsImpl.class);
private static final JavaType GVNIX_REPORTS = new JavaType(
GvNIXReports.class.getName());
/**
* MetadataService offers access to Roo's metadata model, use it to retrieve
* any available metadata by its MID
*/
@Reference
private MetadataService metadataService;
@Reference
private TypeLocationService typeLocationService;
/**
* Use ProjectOperations to install new dependencies, plugins, properties,
* etc into the project configuration
*/
@Reference
private ProjectOperations projectOperations;
/**
* ReportConfigService offers some methods for configuration purposes
*/
@Reference
ReportConfigService reportConfigService;
@Reference
TypeManagementService typeManagementService;
/** {@inheritDoc} */
public boolean isCommandAvailable() {
// Check if a project has been created
return projectOperations.isProjectAvailable(projectOperations
.getFocusedModuleName());
}
/** {@inheritDoc} */
public boolean isProjectAvailable() {
// Check if a project has been created
return projectOperations.isProjectAvailable(projectOperations
.getFocusedModuleName());
}
/** {@inheritDoc} */
public void annotateType(JavaType javaType, String reportName, String format) {
Validate.isTrue(reportConfigService.isSpringMvcProject(),
"Project must be Spring MVC project");
reportConfigService.addJasperReportsViewResolver();
Validate.isTrue(reportConfigService.isJasperViewsProject(),
"WEB-INF/spring/jasper-views.xml must exists");
// Use Roo's Validate type for null checks
Validate.notNull(javaType, "Java type required");
Validate.isTrue(StringUtils.isNotBlank(reportName),
"Report Name required");
Validate.isTrue(StringUtils.isNotBlank(format), "Report Name required");
reportName = reportName.toLowerCase();
format = format.replaceAll(" ", "").toLowerCase();
Validate.isTrue(ReportMetadata.isValidFormat(format),
"Format must be pdf,xls,csv,html");
// Retrieve metadata for the Java source type the annotation is being
// added to
String id = typeLocationService.getPhysicalTypeIdentifier(javaType);
if (id == null) {
throw new IllegalArgumentException("Cannot locate source for '"
+ javaType.getFullyQualifiedTypeName() + "'");
}
// Obtain the physical type and itd mutable details
PhysicalTypeMetadata physicalTypeMetadata = (PhysicalTypeMetadata) metadataService
.get(id);
Validate.notNull(physicalTypeMetadata,
"Java source code unavailable for type "
+ PhysicalTypeIdentifier.getFriendlyName(id));
// Obtain physical type details for the target type
PhysicalTypeDetails physicalTypeDetails = physicalTypeMetadata
.getMemberHoldingTypeDetails();
Validate.notNull(physicalTypeDetails,
"Java source code details unavailable for type "
+ PhysicalTypeIdentifier.getFriendlyName(id));
// Test if the type is an MutableClassOrInterfaceTypeDetails instance so
// the annotation can be added
Validate.isInstanceOf(ClassOrInterfaceTypeDetails.class,
physicalTypeDetails, "Java source code is immutable for type "
+ PhysicalTypeIdentifier.getFriendlyName(id));
ClassOrInterfaceTypeDetails mutableTypeDetails = (ClassOrInterfaceTypeDetails) physicalTypeDetails;
AnnotationMetadata rooWebScaffoldAnnotation = MemberFindingUtils
.getAnnotationOfType(mutableTypeDetails.getAnnotations(),
new JavaType(RooWebScaffold.class.getName()));
if (rooWebScaffoldAnnotation == null) {
logger.warning("The report can not be created over a Controlloer without "
+ "@RooWebScaffold annotation and its 'fromBackingObject' attribute "
+ "set.");
return;
}
// Make a destination list to store our final attributes
List<AnnotationAttributeValue<?>> attributes = new ArrayList<AnnotationAttributeValue<?>>();
List<StringAttributeValue> desiredReports = new ArrayList<StringAttributeValue>();
// Test if the annotation arlready exists on the target type and update
// reports attribute if the new reportName is not defined
AnnotationMetadata gvNixReportsAnnotation = MemberFindingUtils
.getAnnotationOfType(mutableTypeDetails.getAnnotations(),
new JavaType(GvNIXReports.class.getName()));
boolean alreadyAdded = false;
if (gvNixReportsAnnotation != null) {
AnnotationAttributeValue<?> val = gvNixReportsAnnotation
.getAttribute(new JavaSymbolName("value"));
if (val != null) {
// Ensure we have an array of strings
if (!(val instanceof ArrayAttributeValue<?>)) {
logger.warning(getErrorMsg());
return;
}
ArrayAttributeValue<?> arrayVal = (ArrayAttributeValue<?>) val;
for (Object o : arrayVal.getValue()) {
if (!(o instanceof StringAttributeValue)) {
logger.warning(getErrorMsg());
return;
}
StringAttributeValue sv = (StringAttributeValue) o;
if (sv.getValue().equals(reportName + "|" + format)) {
logger.warning("Report " + reportName + " with format "
+ format + " is already defined in "
+ javaType.getSimpleTypeName());
return;
}
if (sv.getValue().contains(reportName + "|")) {
String oldFormat = sv.getValue().split("\\|")[1];
sv = new StringAttributeValue(new JavaSymbolName(
"ignored"), sv.getValue().replace(oldFormat,
ReportMetadata.updateFormat(oldFormat, format)));
alreadyAdded = true;
}
desiredReports.add(sv);
}
}
}
if (!alreadyAdded) {
desiredReports.add(new StringAttributeValue(new JavaSymbolName(
"ignored"), reportName + "|" + format));
}
attributes.add(new ArrayAttributeValue<StringAttributeValue>(
new JavaSymbolName("value"), desiredReports));
AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(
GVNIX_REPORTS, attributes);
ClassOrInterfaceTypeDetailsBuilder mutableTypeDetailsBuilder = new ClassOrInterfaceTypeDetailsBuilder(
mutableTypeDetails);
mutableTypeDetailsBuilder.updateTypeAnnotation(
annotationBuilder.build(), new HashSet<JavaSymbolName>());
typeManagementService
.createOrUpdateTypeOnDisk(mutableTypeDetailsBuilder.build());
}
private String getErrorMsg() {
return "Annotation " + GVNIX_REPORTS.getSimpleTypeName()
+ " attribute 'value' must be an array of strings";
}
}