package org.springframework.roo.addon.dto.addon; import static org.springframework.roo.shell.OptionContexts.PROJECT; import static org.springframework.roo.shell.OptionContexts.UPDATELAST_PROJECT; 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.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.component.ComponentContext; import org.springframework.roo.addon.field.addon.FieldCommands; import org.springframework.roo.classpath.TypeLocationService; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.classpath.scanner.MemberDetailsScanner; import org.springframework.roo.converters.LastUsed; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.JpaJavaType; import org.springframework.roo.model.RooJavaType; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.project.Path; import org.springframework.roo.project.PathResolver; import org.springframework.roo.project.ProjectOperations; import org.springframework.roo.shell.CliAvailabilityIndicator; import org.springframework.roo.shell.CliCommand; import org.springframework.roo.shell.CliOption; import org.springframework.roo.shell.CliOptionAutocompleteIndicator; import org.springframework.roo.shell.CliOptionMandatoryIndicator; import org.springframework.roo.shell.CliOptionVisibilityIndicator; import org.springframework.roo.shell.CommandMarker; import org.springframework.roo.shell.Converter; import org.springframework.roo.shell.ShellContext; import org.springframework.roo.support.logging.HandlerUtils; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; /** * Commands for the DTO add-on to be used by the ROO shell. * * @author Sergio Clares * @since 2.0 */ @Component @Service public class DtoCommands implements CommandMarker { protected final static Logger LOGGER = HandlerUtils.getLogger(FieldCommands.class); // ------------ OSGi component attributes ---------------- private BundleContext context; @Reference private DtoOperations dtoOperations; @Reference private TypeLocationService typeLocationService; @Reference private ProjectOperations projectOperations; @Reference private PathResolver pathResolver; @Reference private FileManager fileManager; @Reference private LastUsed lastUsed; @Reference private MemberDetailsScanner memberDetailsScanner; private Converter<JavaType> javaTypeConverter; protected void activate(final ComponentContext context) { this.context = context.getBundleContext(); } protected void deactivate(final ComponentContext context) { this.context = null; } @CliAvailabilityIndicator({"dto"}) public boolean isDtoCreationAvailable() { return dtoOperations.isDtoCreationPossible(); } @CliAvailabilityIndicator({"entity projection"}) public boolean isEntityProjectionAvailable() { return dtoOperations.isEntityProjectionPossible(); } @CliCommand( value = "dto", help = "Creates a new DTO (Data Transfer Object) class in the directory _src/main/java_ of the selected project module (if any) with @RooDTO annotation.") public void newDtoClass( @CliOption( key = "class", mandatory = true, optionContext = UPDATELAST_PROJECT, help = "The name of the DTO class to create. If you consider it necessary, " + "you can also specify the package (base package can be specified with `~`). " + "Ex.: `--class ~.domain.MyDto`. You can specify module as well, if necessary. " + "Ex.: `--class model:~.domain.MyDto`. When working with a multi-module project, " + "if module is not specified the class will be created in the module which has the focus.") final JavaType name, @CliOption(key = "immutable", mandatory = false, specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", help = "Whether the DTO should be inmutable. " + "Default if option present: `true`; default if option not present: `false`.") final boolean immutable, @CliOption( key = "utilityMethods", mandatory = false, specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", help = "Whether the DTO should implement `toString()`, `hashCode()` and `equals()` methods. " + "Default if option present: `true`; default if option not present: `false`.") final boolean utilityMethods, @CliOption(key = "serializable", mandatory = false, specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", help = "Whether the DTO should implement `java.io.Serializable`. " + "Default if option present: `true`; default if option not present: `false`.") final boolean serializable, @CliOption( key = "entityFormatExpression", mandatory = false, help = "The SpEL expression used to format the entity when showing it in presentation layer e.g. " + "`{#fieldA} {#fieldB}`. It adds the `value` attribute to `io.springlets.format.EntityFormat` " + "annotation.") String formatExpression, @CliOption( key = "entityFormatMessage", mandatory = false, help = "The message key used to obtain a localized SpEL expression to format the entity when " + "showing it in presentation layer. It adds the `message` attribute to " + "`io.springlets.format.EntityFormat` annotation and creates a message in all message bundles " + "with the provided key. Message value should be modified by developer. This kind of format " + "has more priority that 'expression' format added with `--entityFormatExpression`.") String formatMessage, ShellContext shellContext) { // Check if DTO already exists final String entityFilePathIdentifier = pathResolver.getCanonicalPath(name.getModule(), Path.SRC_MAIN_JAVA, name); if (fileManager.exists(entityFilePathIdentifier) && shellContext.isForce()) { fileManager.delete(entityFilePathIdentifier); } else if (fileManager.exists(entityFilePathIdentifier) && !shellContext.isForce()) { throw new IllegalArgumentException( String .format( "DTO '%s' already exists and cannot be created. Try to use a " + "different DTO name on --class parameter or use --force parameter to overwrite it.", name)); } dtoOperations.createDto(name, immutable, utilityMethods, serializable, formatExpression, formatMessage); } /** * Makes 'entity' option mandatory if 'class' has been defined. * * @param shellContext * @return true if 'class' has been defined, false otherwise. */ @CliOptionMandatoryIndicator(command = "entity projection", params = {"entity"}) public boolean isEntityMandatoryForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("class")) { return true; } return false; } /** * Makes 'fields' option mandatory if 'entity' has been defined. * * @param shellContext * @return true if 'entity' has been defined, false otherwise. */ @CliOptionMandatoryIndicator(command = "entity projection", params = {"fields"}) public boolean isFieldsMandatoryForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("entity")) { return true; } return false; } /** * Makes 'all' option visible only if 'class' option is not specified. * * @param shellContext * @return false if 'class' is specified, true otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = {"all"}, help = "Option 'all' can't be used with 'class' option in the same command.") public boolean isAllVisibleForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("class")) { return false; } return true; } /** * Makes 'class' option visible only if 'all' option is not specified. * * @param shellContext * @return false if 'all' is specified, true otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = {"class"}, help = "Option 'class' can't be used with 'all' option in the same command.") public boolean isClassVisibleForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("all")) { return false; } return true; } /** * Makes 'entity' option visible only if 'class' option is already specified. * * @param shellContext * @return true if 'class' is specified, false otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = {"entity"}, help = "Option 'entity' can't be used until 'class' option is specified.") public boolean isEntityVisibleForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("class")) { return true; } return false; } /** * Makes 'suffix' option visible only if 'all' option is specified. * * @param shellContext * @return true if 'all' is specified, false otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = {"suffix"}, help = "Option 'suffix' can't be used if option 'all' isn't already specified.") public boolean isSuffixVisibleForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("all")) { return true; } return false; } /** * Makes 'fields' option visible only if 'entity' option is specified. * * @param shellContext * @return true if 'entity' is specified, false otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = {"fields"}, help = "Option 'fields' can't be used if option 'entity' isn't already specified.") public boolean isFieldsVisibleForEntityProjection(ShellContext shellContext) { // Check already specified params Map<String, String> params = shellContext.getParameters(); if (params.containsKey("entity")) { return true; } return false; } /** * Indicator that checks if `--entityFormatExpression` is visible for `entity projection` command. * This option won't be visible if `--entity` has not already been specified. * * @param shellContext * @return `true` if `--entity` option has been specified, `false` otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = "entityFormatExpression", help = "Option `--entityFormatExpression` is available only if `--entity` has already " + "been specified.") public boolean isEntityFormatExpressionVisibleForEntityProjection(ShellContext shellContext) { if (shellContext.getParameters().get("entity") != null) { return true; } return false; } /** * Indicator that checks if `--entityFormatMessage` is visible for `entity projection` command. * This option won't be visible if `--entity` has not already been specified. * * @param shellContext * @return `true` if `--entity` option has been specified, `false` otherwise. */ @CliOptionVisibilityIndicator(command = "entity projection", params = "entityFormatMessage", help = "Option `--entityFormatExpression` is available only if `--entity` has already " + "been specified.") public boolean isEntityFormatMessageVisibleForEntityProjection(ShellContext shellContext) { if (shellContext.getParameters().get("entity") != null) { return true; } return false; } /** * Find entities in project and returns a list with their fully qualified * names. * * @param shellContext * @return List<String> with available entity full qualified names. */ @CliOptionAutocompleteIndicator(command = "entity projection", param = "entity", help = "Option 'entity' must have an existing entity value. Please, assign it a right value.") public List<String> returnEntityValues(ShellContext shellContext) { // Get current value of class String currentText = shellContext.getParameters().get("entity"); // Create results to return List<String> results = new ArrayList<String>(); // Get entity fully qualified names Set<ClassOrInterfaceTypeDetails> entities = typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_JPA_ENTITY, JpaJavaType.ENTITY); for (ClassOrInterfaceTypeDetails entity : entities) { String name = replaceTopLevelPackageString(entity, currentText); if (!results.contains(name)) { results.add(name); } } return results; } /** * Attempts to obtain entity specified in 'entity' option and returns an * auto-complete list with the entity fields, separated by comma's. * * @param shellContext * @return a List<String> with the possible values. */ @CliOptionAutocompleteIndicator(command = "entity projection", param = "fields", help = "Option fields must have a comma-separated list of valid fields. Please, assign it a " + "correct value. Transient, static and entity collection fields are not valid for " + "projections.", includeSpaceOnFinish = false) public List<String> returnFieldValues(ShellContext shellContext) { List<String> fieldValuesToReturn = new ArrayList<String>(); // Get entity JavaType JavaType entity = getTypeFromEntityParam(shellContext); // Get current fields in --field value String currentFieldValue = shellContext.getParameters().get("fields"); String[] fields = StringUtils.split(currentFieldValue, ","); // Check for bad written separators and return no options if (currentFieldValue.contains(",.") || currentFieldValue.contains(".,")) { return fieldValuesToReturn; } // Check if it is first field if (currentFieldValue.equals("")) { for (FieldMetadata field : getEntityFieldList(entity)) { fieldValuesToReturn.add(field.getFieldName().getSymbolName()); } return fieldValuesToReturn; } // VALIDATION OF CURRENT SPECIFIED VALUES UNTIL LAST MEMBER JavaType lastRelatedEntity = null; String completedValue = ""; List<FieldMetadata> entityFields = null; boolean fieldFound = false; boolean lastFieldIsEntity = false; boolean isMainEntityField = true; for (int i = 0; i < fields.length; i++) { JavaType lastFieldType = entity; // Split field by ".", in case it was a relation field String[] splittedByPeriod = StringUtils.split(fields[i], "."); // Build auto-complete values for (int t = 0; t < splittedByPeriod.length; t++) { fieldFound = false; // Find the field in entity fields if (typeLocationService.getTypeDetails(lastFieldType) != null && typeLocationService.getTypeDetails(lastFieldType).getAnnotation( RooJavaType.ROO_JPA_ENTITY) != null) { entityFields = getEntityFieldList(lastFieldType); } for (FieldMetadata entityField : entityFields) { lastFieldIsEntity = false; if (splittedByPeriod[t].equals(entityField.getFieldName().getSymbolName())) { // Add auto-complete value if (completedValue.equals("")) { completedValue = entityField.getFieldName().getSymbolName(); } else { if (splittedByPeriod.length > 1 && t > 0) { // Field is from a relation completedValue = completedValue.concat(".").concat(entityField.getFieldName().getSymbolName()); } else { // Field is a simple field completedValue = completedValue.concat(",").concat(entityField.getFieldName().getSymbolName()); } } // Record last field JavaType for auto-completing last value lastFieldType = entityField.getFieldType(); // Check if field is an entity different from original entity if (typeLocationService.getTypeDetails(lastFieldType) != null && typeLocationService.getTypeDetails(lastFieldType).getAnnotation( RooJavaType.ROO_JPA_ENTITY) != null && !entityField.getFieldType().equals(entity)) { lastFieldIsEntity = true; lastRelatedEntity = lastFieldType; } fieldFound = true; break; } } // Checks if field to autocomplete is from main entity if (currentFieldValue.endsWith(".") || (splittedByPeriod.length > 1 && !fieldFound)) { isMainEntityField = false; } } } // ADDITION OF NEW VALUES // Add field separator if needed if (fieldFound) { // Always add current value for validation only fieldValuesToReturn.add(completedValue); // If field is entity, append , and . and return values if (lastFieldIsEntity) { fieldValuesToReturn.add(completedValue.concat(",")); fieldValuesToReturn.add(completedValue.concat(".")); if (!currentFieldValue.endsWith(",") && !currentFieldValue.endsWith(".")) { return fieldValuesToReturn; } } } // Build auto-complete values for last member String autocompleteValue = ""; if (isMainEntityField) { // Complete simple fields. Add entity fields as auto-complete values List<FieldMetadata> mainEntityFields = getEntityFieldList(entity); for (FieldMetadata mainEntityField : mainEntityFields) { if (completedValue.equals("")) { // Is first field to complete fieldValuesToReturn.add(mainEntityField.getFieldName().getSymbolName()); } else if (!completedValue.equals("")) { // Check if field is specified and add it if not boolean alreadySpecified = false; // boolean relationField = false; for (int i = 0; i < fields.length; i++) { if (mainEntityField.getFieldName().getSymbolName().equals(fields[i])) { alreadySpecified = true; } } if (!alreadySpecified) { // Add completion autocompleteValue = completedValue.concat(",").concat(mainEntityField.getFieldName().getSymbolName()); } else if (alreadySpecified && typeIsEntity(mainEntityField.getFieldType())) { // Add completion as relation field autocompleteValue = completedValue.concat(",").concat(mainEntityField.getFieldName().getSymbolName()) .concat("."); } // Add completion fieldValuesToReturn.add(autocompleteValue); } } } else if (lastRelatedEntity != null) { // Complete with fields of current relation field List<FieldMetadata> relatedEntityFields = getEntityFieldList(lastRelatedEntity); for (FieldMetadata relatedEntityField : relatedEntityFields) { autocompleteValue = completedValue.concat(".").concat(relatedEntityField.getFieldName().getSymbolName()); // Check if value already exists String additionalValueToAdd = StringUtils.substringAfterLast(autocompleteValue, ","); if (!fieldValuesToReturn.contains(autocompleteValue) && !currentFieldValue.contains(additionalValueToAdd)) { fieldValuesToReturn.add(autocompleteValue); } else if (!fieldValuesToReturn.contains(autocompleteValue) && typeIsEntity(relatedEntityField.getFieldType())) { fieldValuesToReturn.add(autocompleteValue); } else if (additionalValueToAdd.equals("")) { fieldValuesToReturn.add(autocompleteValue); } } } return fieldValuesToReturn; } @CliCommand( value = "entity projection", help = "Creates new projection classes from entities in the directory _src/main/java_ of the " + "selected project module (if any) annotated with `@RooEntityProjection`. Transient, " + "static and entity collection fields are not valid for projections.") public void newProjectionClass( @CliOption( key = "all", mandatory = false, specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", help = "Create one projection class for each entity in the project." + "This option is mandatory if `--class` is not specified. Otherwise, using `--class` will cause the parameter `--all` won't be available.") final boolean all, @CliOption( key = "class", mandatory = false, optionContext = UPDATELAST_PROJECT, help = "The name of the projection class to create. If you consider it necessary, " + "you can also specify the package (base package can be specified with `~`). " + "Ex.: `--class ~.domain.MyProjection`. You can specify module as well, if necessary. " + "Ex.: `--class model:~.domain.MyProjection`. When working with a multi-module " + "project, if module is not specified the projection will be created in the module " + "which has the focus. " + "This option is mandatory if `--all` is not specified. Otherwise, using `--all` will " + "cause the parameter `--class` won't be available.") final JavaType name, @CliOption( key = "entity", mandatory = true, help = "Name of the entity which can be used to create the Projection from. " + "This option is mandatory if `--class` is specified. Otherwise, not specifying `--class` will cause the parameter `--entity` won't be available.") final JavaType entity, @CliOption( key = "fields", mandatory = true, help = "Comma separated list of entity fields to be included into the Projection. " + "Possible values are: non-static, nor transient, nor entity collection fields from " + "main entity or its related entities (only for one-to-one or many-to-one relations). " + "This option is mandatory if `--class` is specified. Otherwise, not specifying " + "`--class` will cause the parameter `--fields` won't be available.") final String fields, @CliOption( key = "suffix", mandatory = false, unspecifiedDefaultValue = "Projection", help = "Suffix added to each Projection class name, built from each associated entity name. " + "This option is only available if `--all` has been already specified. " + "Default if option not present: 'Projection'.") final String suffix, @CliOption( key = "entityFormatExpression", mandatory = false, help = "The SpEL expression used to format the entity when showing it in presentation layer e.g. " + "{#fieldA} {#fieldB}. It adds the `value` attribute to `io.springlets.format.EntityFormat` " + "annotation. " + "This option is available only if `--entity` has been specified.") String formatExpression, @CliOption( key = "entityFormatMessage", mandatory = false, help = "The message key used to obtain a localized SpEL expression to format the entity when " + "showing it in presentation layer. It adds the `message` attribute to " + "`io.springlets.format.EntityFormat` annotation and creates a message in all message bundles " + "with the provided key. Message value should be modified by developer. This kind of format " + "has more priority that 'expression' format added with `--entityFormatExpression`. " + "This option is available only if `--entity` has been specified.") String formatMessage, ShellContext shellContext) { // Check if Projection already exists if (name != null) { final String entityFilePathIdentifier = pathResolver.getCanonicalPath(name.getModule(), Path.SRC_MAIN_JAVA, name); if (fileManager.exists(entityFilePathIdentifier) && shellContext.isForce()) { fileManager.delete(entityFilePathIdentifier); } else if (fileManager.exists(entityFilePathIdentifier) && !shellContext.isForce()) { throw new IllegalArgumentException( String .format( "Projection '%s' already exists and cannot be created. Try to use a " + "different Projection name on --class parameter or use --force parameter to overwrite it.", name)); } } // Check if --fields has a value if (entity != null && StringUtils.isBlank(fields)) { throw new IllegalArgumentException( String .format( "Projection '%s' should have at least one field from its associated entity. Please, add a right value for 'fields' option.", name)); } if (entity != null) { dtoOperations.createProjection(entity, name, fields, null, formatExpression, formatMessage); } else if (all == true) { dtoOperations.createAllProjections(suffix, shellContext); } } /** * Replaces a JavaType fullyQualifiedName for a shorter name using '~' for * TopLevelPackage * * @param cid ClassOrInterfaceTypeDetails of a JavaType * @param currentText String current text for option value * @return the String representing a JavaType with its name shortened */ private String replaceTopLevelPackageString(ClassOrInterfaceTypeDetails cid, String currentText) { String javaTypeFullyQualilfiedName = cid.getType().getFullyQualifiedTypeName(); String javaTypeString = ""; String topLevelPackageString = ""; // Add module value to topLevelPackage when necessary if (StringUtils.isNotBlank(cid.getType().getModule()) && !cid.getType().getModule().equals(projectOperations.getFocusedModuleName())) { // Target module is not focused javaTypeString = cid.getType().getModule().concat(LogicalPath.MODULE_PATH_SEPARATOR); topLevelPackageString = projectOperations.getTopLevelPackage(cid.getType().getModule()) .getFullyQualifiedPackageName(); } else if (StringUtils.isNotBlank(cid.getType().getModule()) && cid.getType().getModule().equals(projectOperations.getFocusedModuleName()) && (currentText.startsWith(cid.getType().getModule()) || cid.getType().getModule() .startsWith(currentText)) && StringUtils.isNotBlank(currentText)) { // Target module is focused but user wrote it javaTypeString = cid.getType().getModule().concat(LogicalPath.MODULE_PATH_SEPARATOR); topLevelPackageString = projectOperations.getTopLevelPackage(cid.getType().getModule()) .getFullyQualifiedPackageName(); } else { // Not multimodule project topLevelPackageString = projectOperations.getFocusedTopLevelPackage().getFullyQualifiedPackageName(); } // Autocomplete with abbreviate or full qualified mode String auxString = javaTypeString.concat(StringUtils.replace(javaTypeFullyQualilfiedName, topLevelPackageString, "~")); if ((StringUtils.isBlank(currentText) || auxString.startsWith(currentText)) && StringUtils.contains(javaTypeFullyQualilfiedName, topLevelPackageString)) { // Value is for autocomplete only or user wrote abbreviate value javaTypeString = auxString; } else { // Value could be for autocomplete or for validation javaTypeString = String.format("%s%s", javaTypeString, javaTypeFullyQualilfiedName); } return javaTypeString; } /** * Gets a list of fields from an entity. Static and transient fields are excluded * as well as collection fields which type is an entity. * * @param entity the JavaType from which to obtain the field list. * @return a List<FieldMetadata> with info of the entity fields. */ private List<FieldMetadata> getEntityFieldList(JavaType entity) { List<FieldMetadata> fieldList = new ArrayList<FieldMetadata>(); ClassOrInterfaceTypeDetails typeDetails = typeLocationService.getTypeDetails(entity); Validate.notNull(typeDetails, String.format( "Cannot find details for class %s. Please be sure that the class exists.", entity.getFullyQualifiedTypeName())); MemberDetails entityMemberDetails = memberDetailsScanner.getMemberDetails(this.getClass().getName(), typeDetails); Validate.notNull( entityMemberDetails.getAnnotation(JpaJavaType.ENTITY), String.format("%s must be an entity to obtain it's fields info.", entity.getFullyQualifiedTypeName())); // Get fields and check for other fields from relations List<FieldMetadata> entityFields = entityMemberDetails.getFields(); for (FieldMetadata field : entityFields) { // Exclude static fields if (Modifier.isStatic(field.getModifier())) { continue; } // Exclude transient fields if (field.getAnnotation(JpaJavaType.TRANSIENT) != null) { continue; } // Exclude entity collection fields JavaType fieldType = field.getFieldType(); if (fieldType.isCommonCollectionType()) { boolean isEntityCollectionField = false; List<JavaType> parameters = fieldType.getParameters(); for (JavaType parameter : parameters) { if (typeIsEntity(parameter)) { isEntityCollectionField = true; break; } } if (isEntityCollectionField) { continue; } } fieldList.add(field); } return fieldList; } /** * Tries to obtain JavaType indicated in command or which has the focus in the * Shell * * @param shellContext the Roo Shell context * @return JavaType or null if no class has the focus or no class is specified * in the command */ private JavaType getTypeFromEntityParam(ShellContext shellContext) { // Try to get 'class' from ShellContext String typeString = shellContext.getParameters().get("entity"); JavaType type = null; if (typeString != null) { type = getJavaTypeConverter().convertFromText(typeString, JavaType.class, PROJECT); } else { type = lastUsed.getJavaType(); } // Inform that entity param couldn't be retrieved Validate.notNull(type, "Couldn't get the entity for 'entity projection' command. Please, be sure that " + "param '--entity' is specified with a right value."); return type; } private boolean typeIsEntity(JavaType type) { return typeLocationService.getTypeDetails(type) != null && typeLocationService.getTypeDetails(type).getAnnotation(RooJavaType.ROO_JPA_ENTITY) != null; } @SuppressWarnings("unchecked") public Converter<JavaType> getJavaTypeConverter() { if (javaTypeConverter == null) { // Get all Services implement JavaTypeConverter interface try { ServiceReference<?>[] references = this.context.getAllServiceReferences(Converter.class.getName(), null); for (ServiceReference<?> ref : references) { Converter<?> converter = (Converter<?>) this.context.getService(ref); if (converter.supports(JavaType.class, PROJECT)) { javaTypeConverter = (Converter<JavaType>) converter; return javaTypeConverter; } } return null; } catch (InvalidSyntaxException e) { LOGGER.warning("ERROR: Cannot load JavaTypeConverter on FieldCommands."); return null; } } else { return javaTypeConverter; } } }