/* * 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.addon.loupefield.addon; import java.io.*; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; 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.Service; import org.gvnix.addon.loupefield.annotations.GvNIXLoupeController; import org.gvnix.support.MessageBundleUtils; import org.gvnix.support.WebProjectUtils; import org.gvnix.support.dependenciesmanager.DependenciesVersionManager; import org.gvnix.web.i18n.roo.addon.ValencianCatalanLanguage; import org.osgi.framework.*; import org.osgi.service.component.ComponentContext; import org.springframework.roo.addon.plural.addon.PluralMetadata; import org.springframework.roo.addon.propfiles.PropFileOperations; import org.springframework.roo.addon.web.mvc.controller.addon.scaffold.WebScaffoldAnnotationValues; import org.springframework.roo.addon.web.mvc.jsp.i18n.I18n; import org.springframework.roo.addon.web.mvc.jsp.i18n.I18nSupport; import org.springframework.roo.addon.web.mvc.jsp.i18n.languages.SpanishLanguage; import org.springframework.roo.classpath.*; import org.springframework.roo.classpath.details.*; import org.springframework.roo.classpath.details.annotations.AnnotationMetadata; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.classpath.persistence.PersistenceMemberLocator; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.classpath.scanner.MemberDetailsScanner; import org.springframework.roo.metadata.MetadataService; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.RooJavaType; import org.springframework.roo.model.SpringJavaType; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.process.manager.MutableFile; import org.springframework.roo.project.*; import org.springframework.roo.support.logging.HandlerUtils; import org.springframework.roo.support.util.*; import org.w3c.dom.*; /** * 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 1.1 */ @Component // Use these Apache Felix annotations to register your commands class in the Roo // container @Service public class LoupefieldOperationsImpl implements LoupefieldOperations { // ------------ OSGi component attributes ---------------- private BundleContext context; private FileManager fileManager; private PathResolver pathResolver; private I18nSupport i18nSupport; private ProjectOperations projectOperations; private PropFileOperations propFileOperations; private TypeLocationService typeLocationService; private TypeManagementService typeManagementService; private MetadataService metadataService; private PersistenceMemberLocator persistenceMemberLocator; private WebProjectUtils webProjectUtils; private MessageBundleUtils messageBundleUtils; private MemberDetailsScanner memberDetailsScanner; private static final Logger LOGGER = HandlerUtils .getLogger(LoupefieldOperationsImpl.class); private static final JavaType ANNOTATION_LOUPE_CONTROLLER = new JavaType( "org.gvnix.addon.loupefield.annotations.GvNIXLoupeController"); private static final JavaType DATATABLES_ANNOTATION = new JavaType( "org.gvnix.addon.datatables.annotations.GvNIXDatatables"); protected void activate(final ComponentContext cContext) { context = cContext.getBundleContext(); } /** {@inheritDoc} */ public boolean isSetupCommandAvailable() { // If jQuery is installed, setup command is available return getProjectOperations().isFeatureInstalledInFocusedModule( "gvnix-jquery") && getProjectOperations().isFeatureInstalledInFocusedModule( "gvnix-datatables") && !getProjectOperations().isFeatureInstalledInFocusedModule( "gvnix-loupe"); } /** {@inheritDoc} */ public boolean isUpdateCommandAvailable() { // If loupefields addon is installed, update command is available return getProjectOperations().isFeatureInstalledInFocusedModule( "gvnix-loupe"); } /** {@inheritDoc} */ public boolean isSetCommandAvailable() { // If loupefields addon is installed, set command is available return getProjectOperations().isFeatureInstalledInFocusedModule( "gvnix-loupe"); } /** {@inheritDoc} */ public void setup() { // Adding tags/loupefield/select.tagx addTagx(); // Adding scripts/loupefield/jquery.loupeField.ext.gvnix.js addLoupeFunctions(); // Adding styles/loupefield/loupeField.css addLoupeStyles(); // Add necessary properties to messages.properties addI18nProperties(); // Include jquery.loupeField.ext.gvnix.js into load-scripts.tagx addToLoadScripts("loupe_js_url", "/resources/scripts/loupefield/jquery.loupeField.ext.gvnix.js", false); // Add style css for components addToLoadScripts("loupe_css_url", "/resources/styles/loupefield/loupeField.css", true); // Add Necessary Dependencies setupProjectPom(); } /** {@inheritDoc} */ public void update() { // Adding tags/loupefield/select.tagx updateTagx(); // Adding scripts/loupefield/jquery.loupeField.ext.gvnix.js updateLoupeFunctions(); // Adding styles/loupefield/loupeField.css updateLoupeStyles(); // Add necessary properties to messages.properties addI18nProperties(); // Include jquery.loupeField.ext.gvnix.js into load-scripts.tagx addToLoadScripts("loupe_js_url", "/resources/scripts/loupefield/jquery.loupeField.ext.gvnix.js", false); // Add loupeField.css on load Scripts addToLoadScripts("loupe_css_url", "/resources/styles/loupefield/loupeField.css", true); // Add Necessary Dependencies setupProjectPom(); } /** {@inheritDoc} */ public void setLoupeController(JavaType controller) { Validate.notNull(controller, "Controller Java Type required"); // Checks if controller is a GvNIXDatatablesController ClassOrInterfaceTypeDetails existingController = getTypeLocationService() .getTypeDetails(controller); AnnotationMetadata datatablesAnnotation = existingController .getAnnotation(DATATABLES_ANNOTATION); Validate.notNull(datatablesAnnotation, controller.getSimpleTypeName() .concat(" must be annotated with @GvNIXDatatables")); // Adding annotation to Controller doAddControllerAnnotation(controller); // Adding uri to create.jspx and update.jspx views updateCreateAndUpdateViews(controller); } /** {@inheritDoc} */ public void setLoupeField(JavaType controller, JavaSymbolName field, String additionalFields, String caption, String baseFilter, String listPath, String max) { Validate.notNull(controller, "Controller Java Type required"); // Getting existing controller and webscaffold annotation values ClassOrInterfaceTypeDetails existingController = getTypeLocationService() .getTypeDetails(controller); WebScaffoldAnnotationValues annotationValues = new WebScaffoldAnnotationValues( existingController); // Checks if controller is annotated with GvNIXLoupeController if (!isControllerAnnotated(existingController)) { LOGGER.log( Level.INFO, "Controller " .concat(controller.getSimpleTypeName()) .concat(" must be annoted with @GvNIXLoupeController. Use 'web mvc loupe set' to annote controller and update views.")); } // Checks if field exists and gets type JavaType fieldType = existsField(annotationValues, field); if (fieldType == null) { return; } // Getting Related entity and its fields ClassOrInterfaceTypeDetails relatedEntity = getTypeLocationService() .getTypeDetails(fieldType); MemberDetails memberDetails = getMemberDetailsScanner() .getMemberDetails(getClass().getName(), relatedEntity); if (relatedEntity == null) { LOGGER.log(Level.INFO, String.format( "Field '%s' could not implements Loupe Field.", StringUtils.uncapitalize(field.getReadableSymbolName()))); return; } List<? extends FieldMetadata> relatedFields = memberDetails.getFields(); if (relatedFields == null) { LOGGER.log(Level.INFO, String.format( "Field '%s' could not implements Loupe Field.", StringUtils.uncapitalize(field.getReadableSymbolName()))); return; } // Checks if additional fields exists as fields in related entity if (!checkIfAdditionalFieldsExists(relatedEntity, relatedFields, additionalFields)) { return; } // Checks if caption field exists as field in related entity if (!checkIfCaptionExists(relatedEntity, relatedFields, caption)) { return; } // Checks if listPath view exists in project if (StringUtils.isNotBlank(listPath)) { if (!existsView(listPath)) { return; } } else { // Checks if there's a controller for the related entity // and if it is annotated with @GvNIXDatatables with standard mode checkRelatedEntity(fieldType); } // Getting identifiers List<FieldMetadata> identifiers = getPersistenceMemberLocator() .getIdentifierFields(fieldType); if (identifiers.isEmpty()) { LOGGER.log( Level.INFO, String.format( "Could not locate any field annoted with @Id for entity '%s'", fieldType.getSimpleTypeName())); return; } // Getting plural final PluralMetadata pluralMetadata = (PluralMetadata) getMetadataService() .get(PluralMetadata.createIdentifier(fieldType, PhysicalTypeIdentifier.getPath(relatedEntity .getDeclaredByMetadataId()))); String plural = pluralMetadata.getPlural().toLowerCase(); // Update field in views create and update updateViews(identifiers, plural, annotationValues.getPath(), field, additionalFields, caption, baseFilter, listPath, "create"); updateViews(identifiers, plural, annotationValues.getPath(), field, additionalFields, caption, baseFilter, listPath, "update"); // Alert if additionalFields is empty, only can search by id if (additionalFields == null) { LOGGER.log( Level.INFO, String.format( "INFO: You don't specify additionalFields, so you can filter by '%s' only", identifiers.get(0).getFieldName().getSymbolName())); } // Creates loupe-callbacks.js and add to load-script.js if not // exists addCallbacksFile(); // Show message to developer with callbacks configuration LOGGER.log( Level.INFO, String.format( "INFO: You can configure callbacks functions for field '%s' editing '%s'. You can add onDraw%s%s function and onSet%s%s function if not exists yet.", field.getSymbolName(), "scripts/loupefield/loupe-callbacks.js", field .getReadableSymbolName(), annotationValues .getFormBackingObject().getSimpleTypeName(), field.getReadableSymbolName(), annotationValues .getFormBackingObject().getSimpleTypeName())); } /** * Checks if there's a controller for the entity and if it's annotated with @GvNIXDatatables * with a correct mode * * @param fieldType */ private void checkRelatedEntity(JavaType fieldType) { // Get all controllers in the project Set<ClassOrInterfaceTypeDetails> controllersDetails = getTypeLocationService() .findClassesOrInterfaceDetailsWithAnnotation( SpringJavaType.CONTROLLER); // Look for all the related entity controllers and store them List<ClassOrInterfaceTypeDetails> relatedControllers = new ArrayList<ClassOrInterfaceTypeDetails>(); for (ClassOrInterfaceTypeDetails controller : controllersDetails) { AnnotationMetadata rooScaffoldAnnotation = controller .getAnnotation(RooJavaType.ROO_WEB_SCAFFOLD); if (rooScaffoldAnnotation != null) { if (fieldType.equals(rooScaffoldAnnotation.getAttribute( "formBackingObject").getValue())) { relatedControllers.add(controller); } } } // Checking if found any controller for the related entity Validate.isTrue( relatedControllers.size() != 0, fieldType .getSimpleTypeName() .concat(" must have a controller annotated with @Controller and @RooWebScaffold")); // Look for controller @GvNIXDatatables and proper mode for loupefield boolean hasDatatablesController = false; boolean standardMode = false; for (ClassOrInterfaceTypeDetails controller : relatedControllers) { AnnotationMetadata datatablesAnnotation = controller .getAnnotation(DATATABLES_ANNOTATION); if (datatablesAnnotation != null) { hasDatatablesController = true; if (datatablesAnnotation.getAttribute("mode") == null || datatablesAnnotation.getAttribute("mode").getValue() .equals("list")) { standardMode = true; break; } } } // If entity hasn't any controller with @GvNIXDatatables, stop execution Validate.isTrue( hasDatatablesController, fieldType .getSimpleTypeName() .concat(" must have a controller annotated with @GvNIXDatatables")); // If entity hasn't any datatables controller in standard mode, stop // execution Validate.isTrue( standardMode, fieldType .getSimpleTypeName() .concat(" must have a @GvNIXDatatables in any other mode than not be 'show'") .concat(" in order to show the loupefield correctly")); } /** * This method adds <code>tags/loupefield/loupe.tagx</code> to the tags * folder */ public void addTagx() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "WEB-INF/tags/loupefield/loupe.tagx"); if (!getFileManager().exists(filePath)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "tag/loupe.tagx"); outputStream = getFileManager().createFile(filePath) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } /** * This method update <code>tags/loupefield/select.tagx</code> with the * current tagx version */ public void updateTagx() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "WEB-INF/tags/loupefield/loupe.tagx"); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils .getInputStream(getClass(), "tag/loupe.tagx"); if (!getFileManager().exists(filePath)) { outputStream = getFileManager().createFile(filePath) .getOutputStream(); } else { outputStream = getFileManager().updateFile(filePath) .getOutputStream(); } IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } /** * This method adds * <code>scripts/loupefield/jquery.loupeField.ext.gvnix.js</code> to the * scripts folder */ public void addLoupeFunctions() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "scripts/loupefield/jquery.loupeField.ext.gvnix.js"); if (!getFileManager().exists(filePath)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "scripts/jquery.loupeField.ext.gvnix.js"); outputStream = getFileManager().createFile(filePath) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } /** * This method adds <code>styles/loupefield/loupeField.css</code> to the * styles folder */ public void addLoupeStyles() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "styles/loupefield/loupeField.css"); if (!getFileManager().exists(filePath)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "styles/loupeField.css"); outputStream = getFileManager().createFile(filePath) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } /** * This method adds <code>scripts/loupefield/loupe-callbacks.js</code> to * the scripts folder and add to load-scripts.js to load in all pages */ public void addCallbacksFile() { // Adding callbacks .js file final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "scripts/loupefield/loupe-callbacks.js"); if (!getFileManager().exists(filePath)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "scripts/loupe-callbacks.js"); outputStream = getFileManager().createFile(filePath) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } // Adding to load-scripts addToLoadScripts("loupe_callbacks_js_url", "/resources/scripts/loupefield/loupe-callbacks.js", false); } } /** * This method updates * <code>scripts/loupefield/jquery.loupeField.ext.gvnix.js</code> with the * current version */ public void updateLoupeFunctions() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "scripts/loupefield/jquery.loupeField.ext.gvnix.js"); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "scripts/jquery.loupeField.ext.gvnix.js"); if (!getFileManager().exists(filePath)) { outputStream = getFileManager().createFile(filePath) .getOutputStream(); } else { outputStream = getFileManager().updateFile(filePath) .getOutputStream(); } IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } /** * This method updates <code>styles/loupefield/loupeField.css</code> to the * styles folder */ public void updateLoupeStyles() { final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "styles/loupefield/loupeField.css"); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils.getInputStream(getClass(), "styles/loupeField.css"); if (!getFileManager().exists(filePath)) { outputStream = getFileManager().createFile(filePath) .getOutputStream(); } else { outputStream = getFileManager().updateFile(filePath) .getOutputStream(); } IOUtils.copy(inputStream, outputStream); } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } /** * This method add necessary properties to messages.properties */ public void addI18nProperties() { // Check if Valencian_Catalan language is supported and add properties // if so Set<I18n> supportedLanguages = getI18nSupport().getSupportedLanguages(); for (I18n i18n : supportedLanguages) { if (i18n.getLocale().equals(new Locale("ca"))) { getMessageBundleUtils().installI18nMessages( new ValencianCatalanLanguage(), getProjectOperations(), getFileManager()); getMessageBundleUtils().addPropertiesToMessageBundle("ca", getClass(), getPropFileOperations(), getProjectOperations(), getFileManager()); break; } } // Add properties to Spanish messageBundle getMessageBundleUtils().installI18nMessages(new SpanishLanguage(), getProjectOperations(), getFileManager()); getMessageBundleUtils().addPropertiesToMessageBundle("es", getClass(), getPropFileOperations(), getProjectOperations(), getFileManager()); // Add properties to default messageBundle getMessageBundleUtils().addPropertiesToMessageBundle("en", getClass(), getPropFileOperations(), getProjectOperations(), getFileManager()); } /** * This method adds reference in laod-script.tagx to use * jquery.loupeField.ext.gvnix.js */ public void addToLoadScripts(String varName, String url, boolean isCss) { // Modify Roo load-scripts.tagx String docTagxPath = getPathResolver().getIdentifier(getWebappPath(), "WEB-INF/tags/util/load-scripts.tagx"); Validate.isTrue(getFileManager().exists(docTagxPath), "load-script.tagx not found: ".concat(docTagxPath)); MutableFile docTagxMutableFile = null; Document docTagx; try { docTagxMutableFile = getFileManager().updateFile(docTagxPath); docTagx = XmlUtils.getDocumentBuilder().parse( docTagxMutableFile.getInputStream()); } catch (Exception e) { throw new IllegalStateException(e); } Element root = docTagx.getDocumentElement(); boolean modified = false; if (isCss) { modified = getWebProjectUtils().addCssToTag(docTagx, root, varName, url) || modified; } else { modified = getWebProjectUtils().addJSToTag(docTagx, root, varName, url) || modified; } if (modified) { XmlUtils.writeXml(docTagxMutableFile.getOutputStream(), docTagx); } } private LogicalPath getWebappPath() { return getWebProjectUtils().getWebappPath(getProjectOperations()); } /** * Annotates given Controller with GvNIXLoupeController * * @param controller */ private void doAddControllerAnnotation(JavaType controller) { Validate.notNull(controller, "Controller required"); // Getting current controller ClassOrInterfaceTypeDetails existingController = getTypeLocationService() .getTypeDetails(controller); // Get @Controller annotation WebScaffoldAnnotationValues annotationValues = new WebScaffoldAnnotationValues( existingController); JavaType entity = annotationValues.getFormBackingObject(); // Validating if is a controller Validate.notNull(entity, "Operation only supported for controllers"); // Checking if is already annoted final boolean isAlreadyAnnotated = MemberFindingUtils .getAnnotationOfType(existingController.getAnnotations(), ANNOTATION_LOUPE_CONTROLLER) != null; if (!isAlreadyAnnotated) { ClassOrInterfaceTypeDetailsBuilder detailsBuilder = new ClassOrInterfaceTypeDetailsBuilder( existingController); AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder( ANNOTATION_LOUPE_CONTROLLER); // Add annotation to target type detailsBuilder.addAnnotation(annotationBuilder.build()); // Save changes to disk getTypeManagementService().createOrUpdateTypeOnDisk( detailsBuilder.build()); } } /** * Update project pom: install repositories and dependencies */ private void setupProjectPom() { // Get add-on configuration file Element configuration = XmlUtils.getConfiguration(getClass()); // Install the add-on repository needed List<Element> repos = XmlUtils.findElements( "/configuration/gvnix/repositories/repository", configuration); for (Element repo : repos) { getProjectOperations().addRepositories( getProjectOperations().getFocusedModuleName(), Collections.singleton(new Repository(repo))); } // Install properties List<Element> properties = XmlUtils.findElements( "/configuration/gvnix/properties/*", configuration); for (Element property : properties) { getProjectOperations().addProperty( getProjectOperations().getFocusedModuleName(), new Property(property)); } // Install dependencies List<Element> depens = XmlUtils.findElements( "/configuration/gvnix/dependencies/dependency", configuration); DependenciesVersionManager.manageDependencyVersion( getMetadataService(), getProjectOperations(), depens); } /** * This method updates create and update views adding loupefield uri * * @param controller */ private void updateCreateAndUpdateViews(JavaType controller) { Map<String, String> uriMap = new HashMap<String, String>(1); uriMap.put("xmlns:loupefield", "urn:jsptagdir:/WEB-INF/tags/loupefield"); ClassOrInterfaceTypeDetails existingController = getTypeLocationService() .getTypeDetails(controller); WebScaffoldAnnotationValues annotationValues = new WebScaffoldAnnotationValues( existingController); updateJspx(controller, annotationValues.getPath(), uriMap, "create"); updateJspx(controller, annotationValues.getPath(), uriMap, "update"); } /** * @param controller * @param controllerPath * @param uriMap * @param jspxName */ private void updateJspx(JavaType controller, String controllerPath, Map<String, String> uriMap, String jspxName) { Validate.notBlank(controllerPath, "Path is not specified in the @RooWebScaffold annotation for '" + controller.getSimpleTypeName() + "'"); Validate.isTrue(controllerPath != null && !controllerPath.isEmpty(), "Path is not specified in the @RooWebScaffold annotation for '" + controller.getSimpleTypeName() + "'"); if (controllerPath != null) { getWebProjectUtils().addTagxUriInJspx(controllerPath, jspxName, uriMap, getProjectOperations(), getFileManager()); } } /** * This method checks if a view exists in the project * * @param path * @return */ private boolean existsView(String path) { String viewFileName = path.concat(".jspx"); final String filePath = getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, "WEB-INF/views/".concat(viewFileName)); if (!getFileManager().exists(filePath)) { LOGGER.log( Level.INFO, "View '".concat(viewFileName).concat( "' doesn't exists in proyect.")); return false; } return true; } /** * This method checks if controller is annotated with @GvNIXLoupeController * * @param controller * @return */ private boolean isControllerAnnotated(ClassOrInterfaceTypeDetails controller) { AnnotationMetadata annotations = controller.getAnnotation(new JavaType( GvNIXLoupeController.class)); if (annotations == null) { return false; } return true; } /** * This method checks if field exists in the Controller related entity * * @param controller * @return */ private JavaType existsField(WebScaffoldAnnotationValues annotationValues, JavaSymbolName field) { JavaType entity = annotationValues.getFormBackingObject(); final ClassOrInterfaceTypeDetails cid = getTypeLocationService() .getTypeDetails(entity); MemberDetails memberDetails = getMemberDetailsScanner() .getMemberDetails(getClass().getName(), cid); if (cid == null) { LOGGER.log(Level.INFO, "Controller Entity cannnot be resolved to a type in your project"); return null; } List<? extends FieldMetadata> fieldList = memberDetails.getFields(); Iterator<? extends FieldMetadata> it = fieldList.iterator(); JavaType fieldType = null; boolean exists = false; while (it.hasNext()) { FieldMetadata currentField = it.next(); if (field.getReadableSymbolName().equals( currentField.getFieldName().getReadableSymbolName())) { fieldType = currentField.getFieldType(); exists = true; } } if (!exists) { LOGGER.log(Level.INFO, "The field '" + field.getSymbolName() + "' can not be resolved as field of your entity."); return null; } return fieldType; } /** * This method checks if all additionalField exists * * @param entity * @param relatedFields * @param additionalFields */ private boolean checkIfAdditionalFieldsExists( ClassOrInterfaceTypeDetails entity, List<? extends FieldMetadata> relatedFields, String additionalFields) { if (StringUtils.isNotBlank(additionalFields)) { String[] additionalFieldsList = additionalFields.split(","); for (int i = 0; i < additionalFieldsList.length; i++) { boolean exists = false; Iterator<? extends FieldMetadata> it = relatedFields.iterator(); while (it.hasNext()) { FieldMetadata relatedField = it.next(); String additionalField = additionalFieldsList[i]; String relatedFieldName = StringUtils .uncapitalize(relatedField.getFieldName() .getSymbolName()); if (relatedFieldName.equals(additionalField)) { exists = true; } } if (!exists) { LOGGER.log( Level.INFO, String.format( "Additional field '%s' doesn't exists in related entity '%s'", additionalFieldsList[i], entity.getName() .getSimpleTypeName())); return false; } } } return true; } /** * This method checks if caption exists as a related field * * @param entity * @param relatedFields * @param caption * @return */ private boolean checkIfCaptionExists(ClassOrInterfaceTypeDetails entity, List<? extends FieldMetadata> relatedFields, String caption) { if (StringUtils.isNotBlank(caption)) { boolean exists = false; Iterator<? extends FieldMetadata> it = relatedFields.iterator(); while (it.hasNext()) { FieldMetadata relatedField = it.next(); String relatedFieldName = StringUtils.uncapitalize(relatedField .getFieldName().getSymbolName()); if (relatedFieldName.equals(caption)) { exists = true; } } if (!exists) { LOGGER.log( Level.INFO, String.format( "Caption field '%s' doesn't exists in related entity '%s'", caption, entity.getName().getSimpleTypeName())); return false; } } return true; } /** * This method update field in view to use loupe element * * @param controller * @param path * @param field * @param additionalFields * @param caption * @param baseFilter * @param listPath * @param max */ private void updateViews(List<FieldMetadata> identifiers, String entityPlural, String path, JavaSymbolName field, String additionalFields, String caption, String baseFilter, String listPath, String viewName) { String relativePath = "WEB-INF/views/".concat(path).concat("/") .concat(viewName).concat(".jspx"); String docJspx = getPathResolver().getIdentifier( getWebProjectUtils().getWebappPath(getProjectOperations()), relativePath); Document docJspXml = getWebProjectUtils().loadXmlDocument(docJspx, getFileManager()); if (docJspXml == null) { LOGGER.log(Level.INFO, "Could not locate file '".concat(relativePath).concat("'")); return; } Element docRoot = docJspXml.getDocumentElement(); Element form = XmlUtils.findFirstElement( String.format("/div/%s", viewName), docRoot); Element element = XmlUtils.findFirstElement( String.format("/div/%s/*[@field='%s']", viewName, StringUtils.uncapitalize(field.getSymbolName())), docRoot); if (element == null) { LOGGER.log(Level.INFO, String.format( "Could not locate field '%s' on '%s/%s'", StringUtils.uncapitalize(field.getSymbolName()), path, viewName)); return; } // Creating loupe element Element loupe = docJspXml.createElement("loupefield:loupe"); // Copying element attributes to new loupe element NamedNodeMap elementAttributes = element.getAttributes(); for (int i = 0; i < elementAttributes.getLength(); i++) { Node attr = elementAttributes.item(i); loupe.setAttribute(attr.getNodeName(), attr.getNodeValue()); } // Changing z value loupe.setAttribute("z", "user-managed"); // Adding pkField String pkField = identifiers.get(0).getFieldName().getSymbolName(); loupe.setAttribute("pkField", pkField); // Adding controllerPath loupe.setAttribute("controllerPath", path); // Adding additionalFields attribute if (StringUtils.isNotBlank(additionalFields)) { loupe.setAttribute("additionalFields", additionalFields); } // Adding caption Attribute if (StringUtils.isNotBlank(caption)) { loupe.setAttribute("caption", caption); } // Adding baseFilter if (StringUtils.isNotBlank(baseFilter)) { loupe.setAttribute("baseFilter", baseFilter); } // Adding listPath if (StringUtils.isNotBlank(listPath)) { loupe.setAttribute("listPath", listPath); } else { String entityPath = entityPlural.concat("/list"); loupe.setAttribute("listPath", entityPath); } // Adding mode loupe.setAttribute("mode", viewName); // Remove deprecated attributes if (loupe.getAttribute("itemValue") != "") { loupe.removeAttribute("itemValue"); } if (loupe.getAttribute("items") != "") { loupe.removeAttribute("items"); } // Append new loupe element to view form.appendChild(loupe); // Remove old element form.removeChild(element); DomUtils.removeTextNodes(docJspXml); getFileManager().createOrUpdateTextFileIfRequired(docJspx, XmlUtils.nodeToString(docJspXml), true); } /*** * FEATURE METHODS */ @Override public String getName() { return FEATURE_NAME_GVNIX_LOUPEFIELDS; } @Override public boolean isInstalledInModule(String moduleName) { String dirPath = getPathResolver().getIdentifier(getWebappPath(), "scripts/loupefield/jquery.loupeField.ext.gvnix.js"); return getFileManager().exists(dirPath); } public FileManager getFileManager() { if (fileManager == null) { // Get all Services implement FileManager interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences(FileManager.class.getName(), null); for (ServiceReference<?> ref : references) { return (FileManager) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load FileManager on LoupeFieldOperationsImpl."); return null; } } else { return fileManager; } } public PathResolver getPathResolver() { if (pathResolver == null) { // Get all Services implement PathResolver interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences(PathResolver.class.getName(), null); for (ServiceReference<?> ref : references) { return (PathResolver) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load PathResolver on LoupeFieldOperationsImpl."); return null; } } else { return pathResolver; } } public I18nSupport getI18nSupport() { if (i18nSupport == null) { // Get all Services implement I18nSupport interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences(I18nSupport.class.getName(), null); for (ServiceReference<?> ref : references) { return (I18nSupport) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load I18nSupport on LoupeFieldOperationsImpl."); return null; } } else { return i18nSupport; } } public ProjectOperations getProjectOperations() { if (projectOperations == null) { // Get all Services implement ProjectOperations interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( ProjectOperations.class.getName(), null); for (ServiceReference<?> ref : references) { return (ProjectOperations) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load ProjectOperations on LoupeFieldOperationsImpl."); return null; } } else { return projectOperations; } } public PropFileOperations getPropFileOperations() { if (propFileOperations == null) { // Get all Services implement PropFileOperations interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( PropFileOperations.class.getName(), null); for (ServiceReference<?> ref : references) { return (PropFileOperations) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load PropFileOperations on LoupeFieldOperationsImpl."); return null; } } else { return propFileOperations; } } public TypeLocationService getTypeLocationService() { if (typeLocationService == null) { // Get all Services implement TypeLocationService interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( TypeLocationService.class.getName(), null); for (ServiceReference<?> ref : references) { return (TypeLocationService) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load TypeLocationService on LoupeFieldOperationsImpl."); return null; } } else { return typeLocationService; } } public TypeManagementService getTypeManagementService() { if (typeManagementService == null) { // Get all Services implement TypeManagementService interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( TypeManagementService.class.getName(), null); for (ServiceReference<?> ref : references) { return (TypeManagementService) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load TypeManagementService on LoupeFieldOperationsImpl."); return null; } } else { return typeManagementService; } } public MetadataService getMetadataService() { if (metadataService == null) { // Get all Services implement MetadataService interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( MetadataService.class.getName(), null); for (ServiceReference<?> ref : references) { return (MetadataService) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load MetadataService on LoupeFieldOperationsImpl."); return null; } } else { return metadataService; } } public PersistenceMemberLocator getPersistenceMemberLocator() { if (persistenceMemberLocator == null) { // Get all Services implement PersistenceMemberLocator interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( PersistenceMemberLocator.class.getName(), null); for (ServiceReference<?> ref : references) { return (PersistenceMemberLocator) this.context .getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load PersistenceMemberLocator on LoupeFieldOperationsImpl."); return null; } } else { return persistenceMemberLocator; } } public WebProjectUtils getWebProjectUtils() { if (webProjectUtils == null) { // Get all Services implement WebProjectUtils interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( WebProjectUtils.class.getName(), null); for (ServiceReference<?> ref : references) { webProjectUtils = (WebProjectUtils) this.context .getService(ref); return webProjectUtils; } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load WebProjectUtils on LoupeFieldOperationsImpl."); return null; } } else { return webProjectUtils; } } public MessageBundleUtils getMessageBundleUtils() { if (messageBundleUtils == null) { // Get all Services implement MessageBundleUtils interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( MessageBundleUtils.class.getName(), null); for (ServiceReference<?> ref : references) { messageBundleUtils = (MessageBundleUtils) this.context .getService(ref); return messageBundleUtils; } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load MessageBundleUtils on LoupeFieldOperationsImpl."); return null; } } else { return messageBundleUtils; } } public MemberDetailsScanner getMemberDetailsScanner() { if (memberDetailsScanner == null) { // Get all Services implement MemberDetailsScanner interface try { ServiceReference<?>[] references = this.context .getAllServiceReferences( MemberDetailsScanner.class.getName(), null); for (ServiceReference<?> ref : references) { return (MemberDetailsScanner) this.context.getService(ref); } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("Cannot load MemberDetailsScanner on " .concat(getClass().getSimpleName())); return null; } } else { return memberDetailsScanner; } } }