/*
* 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.exception.handler.roo.addon.addon;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.ItdTypeDetailsBuilder;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.classpath.itd.ItdSourceFileComposer;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.support.util.FileUtils;
/**
* This type produces metadata for a new ITD. It uses an
* {@link ItdTypeDetailsBuilder} provided by
* {@link AbstractItdTypeDetailsProvidingMetadataItem} to register a field in
* the ITD and a new method.
*
* @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.8
*/
public class ModalDialogMetadata extends
AbstractItdTypeDetailsProvidingMetadataItem {
private static final String PROVIDES_TYPE_STRING = ModalDialogMetadata.class
.getName();
private static final String PROVIDES_TYPE = MetadataIdentificationUtils
.create(PROVIDES_TYPE_STRING);
private final MemberDetails memberDetails;
private final FileManager fileManager;
private final PathResolver pathResolver;
public ModalDialogMetadata(String identifier, JavaType aspectName,
PhysicalTypeMetadata governorPhysicalTypeMetadata,
MemberDetails memberDetails, List<String> definedModalDialogs,
FileManager fileManager, PathResolver pathResolver) {
super(identifier, aspectName, governorPhysicalTypeMetadata);
Validate.isTrue(isValid(identifier), "Metadata identification string '"
+ identifier + "' does not appear to be a valid");
Validate.notNull(memberDetails, "MemberDetails must be not null");
this.memberDetails = memberDetails;
this.fileManager = fileManager;
this.pathResolver = pathResolver;
for (String modalDialog : definedModalDialogs) {
installModalDialogSample(modalDialog);
builder.addMethod(getCustomModalDialogMethod(modalDialog));
}
builder.addMethod(getModalDialogMethod());
itdTypeDetails = builder.build();
new ItdSourceFileComposer(itdTypeDetails);
}
/**
* Returns MethodMetadata for:
* <p>
* <code>modalDialog(DialogType dialogType, String title, String description, HttpServletRequest httpServletRequest)</code>
*
* @param aspectName
* @return
*/
private MethodMetadata getModalDialogMethod() {
// Specify the desired method name
JavaSymbolName methodName = new JavaSymbolName("modalDialog");
List<AnnotatedJavaType> paramTypes = getMethodParamTypes(false);
MethodMetadata modalDialogMethod = methodExists(methodName, paramTypes);
if (modalDialogMethod != null) {
return modalDialogMethod;
}
List<JavaSymbolName> paramNames = getMethodParamNames(false);
InvocableMemberBodyBuilder bodyBuilder = getMethodBodyBuilder(null);
return new MethodMetadataBuilder(getId(), 0, methodName,
JavaType.VOID_PRIMITIVE, paramTypes, paramNames, bodyBuilder)
.build();
}
/**
* Returns MethodMetadata for:
* <p>
* <code>nameOfModalDialog(DialogType dialogType, String title, String description, HashMap<String, Object> params, HttpServletRequest httpServletRequest)</code>
*
* @param aspectName
* @return
*/
private MethodMetadata getCustomModalDialogMethod(String modalDialog) {
// Specify the desired method name
JavaSymbolName methodName = new JavaSymbolName(modalDialog);
List<AnnotatedJavaType> paramTypes = getMethodParamTypes(true);
MethodMetadata modalDialogMethod = methodExists(methodName, paramTypes);
if (modalDialogMethod != null) {
return modalDialogMethod;
}
List<JavaSymbolName> paramNames = getMethodParamNames(true);
InvocableMemberBodyBuilder bodyBuilder = getMethodBodyBuilder(modalDialog);
return new MethodMetadataBuilder(getId(), 0, methodName,
JavaType.VOID_PRIMITIVE, paramTypes, paramNames, bodyBuilder)
.build();
}
/**
* Returns the list of method parameter types.
*
* @param addParamsParameter if true will define
* <code>HashMap<String, Object> params</code> parameter
* @return
*/
private List<AnnotatedJavaType> getMethodParamTypes(
boolean addParamsParameter) {
List<AnnotatedJavaType> paramTypes = new ArrayList<AnnotatedJavaType>();
JavaType dialogType = getJavaTypeForClassName("DialogType");
paramTypes.add(new AnnotatedJavaType(dialogType,
new ArrayList<AnnotationMetadata>()));
paramTypes.add(new AnnotatedJavaType(JavaType.STRING,
new ArrayList<AnnotationMetadata>()));
paramTypes.add(new AnnotatedJavaType(JavaType.STRING,
new ArrayList<AnnotationMetadata>()));
if (addParamsParameter) {
List<JavaType> typeParams = new ArrayList<JavaType>();
typeParams.add(JavaType.STRING);
typeParams.add(new JavaType("java.lang.Object"));
JavaType hashMap = new JavaType("java.util.HashMap", 0,
DataType.TYPE, null, typeParams);
paramTypes.add(new AnnotatedJavaType(hashMap,
new ArrayList<AnnotationMetadata>()));
}
paramTypes.add(new AnnotatedJavaType(new JavaType(
"javax.servlet.http.HttpServletRequest"),
new ArrayList<AnnotationMetadata>()));
return paramTypes;
}
/**
* Returns the list of method parameter names.
*
* @param addParamsParameter if true will define <code>params</code>
* parameter
* @return
*/
private List<JavaSymbolName> getMethodParamNames(boolean addParamsParameter) {
List<JavaSymbolName> paramNames = new ArrayList<JavaSymbolName>();
paramNames.add(new JavaSymbolName("dialogType"));
paramNames.add(new JavaSymbolName("title"));
paramNames.add(new JavaSymbolName("description"));
if (addParamsParameter) {
paramNames.add(new JavaSymbolName("params"));
}
paramNames.add(new JavaSymbolName("httpServletRequest"));
return paramNames;
}
/**
* Returns the InvocableMemeberBodyBuilder for Dialog create and set in
* Session methods
*
* @param customModalDialogJspxName if informed will use
* <code>Dialog(DialogType dialogType, String page, String title, String description, HashMap<String, Object> params)</code>
* constructor
* @return
*/
private InvocableMemberBodyBuilder getMethodBodyBuilder(
String customModalDialogJspxName) {
InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
JavaType httpSession = new JavaType("javax.servlet.http.HttpSession");
bodyBuilder.appendFormalLine(httpSession
.getNameIncludingTypeParameters(false,
builder.getImportRegistrationResolver()).concat(
" session = httpServletRequest.getSession();"));
JavaType modalDialogJavaType = getJavaTypeForClassName("Dialog");
if (StringUtils.isNotBlank(customModalDialogJspxName)) {
bodyBuilder
.appendFormalLine(modalDialogJavaType
.getNameIncludingTypeParameters(false,
builder.getImportRegistrationResolver())
.concat(" modalDialog = new Dialog(dialogType, \"/WEB-INF/dialogs/"
.concat(customModalDialogJspxName)
.concat(".jspx\", title, description, params);")));
}
else {
bodyBuilder
.appendFormalLine(modalDialogJavaType
.getNameIncludingTypeParameters(false,
builder.getImportRegistrationResolver())
.concat(" modalDialog = new Dialog(dialogType, title, description);"));
}
bodyBuilder
.appendFormalLine("session.setAttribute(\"dialogMessage\", modalDialog);");
return bodyBuilder;
}
private MethodMetadata methodExists(JavaSymbolName methodName,
List<AnnotatedJavaType> parameters) {
return memberDetails.getMethod(methodName,
AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameters));
}
/**
* Returns the JavaType for the given className. It gets the fully qualified
* package name from the aspect of this metadata, that is the aspect of a
* Controller class.
* <p>
* Since the add-on installs Dialog class in subpackage
* <code>controller_package.servlet.handler</code>, it builds the package
* name adding <code>.dialog.[className]</code> to the aspect package.
* <p>
* DialogType is special because is an enum type defined in Dialog class
*
* @param className Right now it expects only "DialogType" or "Dialog"
* @return
*/
private JavaType getJavaTypeForClassName(String className) {
String typePackage = aspectName.getPackage()
.getFullyQualifiedPackageName().concat(".dialog.");
if (className.equals("DialogType")) {
return new JavaType(typePackage.concat("Dialog.").concat(className));
}
return new JavaType(typePackage.concat(className));
}
private void installModalDialogSample(String modalDialog) {
String destinationPath = pathResolver.getIdentifier(
LogicalPath.getInstance(Path.SRC_MAIN_WEBAPP, ""),
"WEB-INF/dialogs/".concat(modalDialog).concat(".jspx"));
if (!fileManager.exists(destinationPath)) {
String template;
try {
InputStream templateInputStream = FileUtils.getInputStream(
getClass(), "modaldialog.jspx-template");
InputStreamReader readerFile = new InputStreamReader(
templateInputStream);
template = IOUtils.toString(readerFile);
}
catch (IOException ioe) {
throw new IllegalStateException(
"Unable load ITD jspx template", ioe);
}
try {
if (template.length() > 0) {
MutableFile mutableJspx = fileManager
.createFile(destinationPath);
if (mutableJspx != null) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = IOUtils.toInputStream(template);
outputStream = mutableJspx.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
}
}
catch (IOException ioe) {
throw new IllegalStateException("Could not create ".concat(
modalDialog).concat(".jspx file"), ioe);
}
}
// else {
// logger.warning(modalDialog
// .concat(".jspx already exists. We keep previous file"));
// }
}
public static final String getMetadataIdentiferType() {
return PROVIDES_TYPE;
}
public static final String createIdentifier(JavaType javaType,
LogicalPath path) {
return PhysicalTypeIdentifierNamingUtils.createIdentifier(
PROVIDES_TYPE_STRING, javaType, path);
}
public static final JavaType getJavaType(String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getJavaType(
PROVIDES_TYPE_STRING, metadataIdentificationString);
}
public static final LogicalPath getPath(String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static boolean isValid(String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
}