/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.report.application; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.design.JRValidationFault; import net.sf.jasperreports.engine.design.JRVerifier; import net.sf.jasperreports.engine.design.JasperDesign; import net.sf.jasperreports.engine.xml.JRXmlLoader; import org.apache.commons.lang.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import com.globant.katari.core.application.ValidatableCommand; import com.globant.katari.hibernate.coreuser.domain.Role; import com.globant.katari.hibernate.coreuser.domain.RoleRepository; import com.globant.katari.report.domain.JasperReportRepository; import com.globant.katari.report.domain.ReportDefinition; /** * Command for saving or updating a Report Definitions. */ public class SaveReportCommand implements ValidatableCommand<Void> { /** * The class logger. */ private static Logger log = LoggerFactory.getLogger(SaveReportCommand.class); /** The Report Definition repository. It is never null. */ private JasperReportRepository jasperReportRepository; /** The Role repository. It is never null. */ private RoleRepository roleRepository; /** * The report definition id. The default value (zero) represents that the * Report Definition a new one. */ private long reportId = 0; /** * The report definition name. It is never null. */ private String name = ""; /** * The report definition description. It is never null. */ private String description = ""; /** * The byte array with the report XML template file. It is null when the user * does not specify a file to upload. */ private byte[] reportContent = null; /** * The roles ids. */ private List<String> roleIds = new ArrayList<String>(); /** * The list of all roles available in the system. It is null . */ private List<Role> availableRoles = null; /** * Creates a new SaveClientCommand object with a clientRepository dependency. * * @param theReportDefinition the client repository. It cannot be null. * * @param theRoleRepository The role repository. It cannot be null. */ public SaveReportCommand(final JasperReportRepository theReportDefinition, final RoleRepository theRoleRepository) { Validate.notNull(theReportDefinition, "The report repository cannot be null"); Validate.notNull(theRoleRepository, "The role repository cannot be null"); jasperReportRepository = theReportDefinition; roleRepository = theRoleRepository; } /** * Gets the report id. * * @return the reportId. */ public long getReportId() { return reportId; } /** * Sets the report id. * * @param theReportId the reportId to set. 0 for a new report. */ public void setReportId(final long theReportId) { reportId = theReportId; } /** * Gets the report Content. * * @return the report Content, null for a new report or when the user is not * modifying the content. */ public byte[] getReportContent() { return reportContent; } /** * Sets the byte[] from the report template XML file. * * @param content the array of bytes containing the report template XML file. * It can be null, but see validate(). */ public void setReportContent(final byte[] content) { reportContent = content.clone(); } /** * Gets the name of the report. * * @return the name, it never returns null. */ public String getName() { return name; } /** * Sets of the name of the report definition. * * @param theName the name to set. It cannot be null. */ public void setName(final String theName) { Validate.notNull(theName, "The report name cannot be null."); name = theName; } /** * Gets the description of the report. * * @return the report description, it never returns null. */ public String getDescription() { return description; } /** * Sets of the description of the report definition. * * @param theDescription the description to set. It cannot be null. */ public void setDescription(final String theDescription) { Validate.notNull(theDescription, "The description cannot be null."); description = theDescription; } /** * Gets the ids of the roles. * * @return Returns the ids of the user's roles. It cannot be null. */ public List<String> getRoleIds() { return roleIds; } /** * Sets the ids of the roles. * * @param theRoleIds The roles id. If it is null, the report has no roles. */ public void setRoleIds(final List<String> theRoleIds) { roleIds = theRoleIds; if (roleIds == null) { roleIds = new ArrayList<String>(); } } /** Obtains the a map of ids to its corresponding role. * * @return the map of roles, never returns null. */ public Map<String, String> getAvailableRoles() { // User Roles. Map<String, String> rolesMap = new LinkedHashMap<String, String>(); for (Role role : availableRoles) { rolesMap.put(String.valueOf(role.getId()), role.getName()); } return rolesMap; } /** * Initializes the command. * * During initialization, if a report id is specified, the corresponding * report definition will be retrieved from the repository. This report must * exist in the repository. */ public void init() { availableRoles = roleRepository.getRoles(); if (reportId != 0) { ReportDefinition report; report = jasperReportRepository.findReportDefinitionById(reportId); if (report == null) { throw new RuntimeException("The specified report id could not be" + " found"); } name = report.getName(); description = report.getDescription(); List<String> rolesId = new ArrayList<String>(); for (Role role : report.getRoles()) { rolesId.add(String.valueOf(role.getId())); } setRoleIds(rolesId); } } /** * Saves the domain <code>ReportDefinition</code>. * * @return The return value is meaningless. */ public Void execute() { log.trace("entering execute"); ReportDefinition aReportDefinition; List<Role> newRoles = roleRepository.getRoles(roleIds); if (reportId == 0) { aReportDefinition = new ReportDefinition(name, description, reportContent); } else { aReportDefinition = jasperReportRepository .findReportDefinitionById(reportId); aReportDefinition.modify(name, description); if (reportContent != null) { // The user uploaded a file. aReportDefinition.setReportContent(reportContent); } // Remove existing roles. aReportDefinition.getRoles().clear(); } // Add the new roles. for (Role role : newRoles) { aReportDefinition.addRole(role); } jasperReportRepository.save(aReportDefinition); return null; } /** * Validates this command. * * The name must not be empty. For a new report, the report content is * required. * * @param errors Contextual state about the validation process. It cannot be * null. */ @SuppressWarnings("unchecked") public void validate(final Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "error.required"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "description", "error.required"); long existentId = jasperReportRepository.findIdForName(name); boolean nameIsTaken = existentId != 0 && reportId != existentId; if (nameIsTaken) { // The name is duplicated. errors.rejectValue("name", "error.usedName", new String[]{name}, ""); } if (reportContent != null && reportContent.length == 0) { // The user uploaded an empty file. errors.rejectValue("reportContent", "error.notempty"); } if (reportContent == null && reportId == 0) { // The user did not upload a file for a new report. errors.rejectValue("reportContent", "error.required"); } if (reportContent != null) { // Verifies if the report can be parsed by jasper. try { // try to convert the file uploaded by the user into a JasperDesign. // If it not valid it will throw an JRExeption. JasperDesign design; design = JRXmlLoader.load(new ByteArrayInputStream(reportContent)); // At this point we are sure that the file uploaded by the user is a // valid XML but it still can contains some design problems. Therefore // it is further validated. List<JRValidationFault> faults; faults = (List<JRValidationFault>) JRVerifier.verifyDesign(design); // it adds an error for every validation fault found by the former // validator. for (JRValidationFault fault : faults) { errors.rejectValue("reportContent", "error.invalidReport", new String[] {fault.getMessage()}, "error.invalidReport"); } } catch (JRException e) { errors.rejectValue("reportContent", "error.invalidXml"); } } } }