/** * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.workbench.common.services.datamodeller.driver.impl; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.drools.workbench.models.datamodel.oracle.Annotation; import org.drools.workbench.models.datamodel.oracle.ModelField; import org.drools.workbench.models.datamodel.oracle.ProjectDataModelOracle; import org.drools.workbench.models.datamodel.oracle.TypeSource; import org.kie.workbench.common.services.datamodel.backend.server.DataModelOracleUtilities; import org.kie.workbench.common.services.datamodeller.codegen.GenerationContext; import org.kie.workbench.common.services.datamodeller.codegen.GenerationEngine; import org.kie.workbench.common.services.datamodeller.codegen.GenerationListener; import org.kie.workbench.common.services.datamodeller.core.AnnotationDefinition; import org.kie.workbench.common.services.datamodeller.core.DataModel; import org.kie.workbench.common.services.datamodeller.core.DataObject; import org.kie.workbench.common.services.datamodeller.core.ObjectProperty; import org.kie.workbench.common.services.datamodeller.core.ObjectSource; import org.kie.workbench.common.services.datamodeller.core.Visibility; import org.kie.workbench.common.services.datamodeller.core.impl.ModelFactoryImpl; import org.kie.workbench.common.services.datamodeller.driver.AnnotationDriver; import org.kie.workbench.common.services.datamodeller.driver.FileChangeDescriptor; import org.kie.workbench.common.services.datamodeller.driver.ModelDriver; import org.kie.workbench.common.services.datamodeller.driver.ModelDriverException; import org.kie.workbench.common.services.datamodeller.driver.ModelDriverListener; import org.kie.workbench.common.services.datamodeller.driver.model.ModelDriverResult; import org.kie.workbench.common.services.datamodeller.driver.impl.annotations.CommonAnnotations; import org.kie.workbench.common.services.datamodeller.util.DriverUtils; import org.kie.workbench.common.services.datamodeller.util.NamingUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.uberfire.io.IOService; import org.uberfire.java.nio.file.OpenOption; public class DataModelOracleModelDriver implements ModelDriver { private static final Logger logger = LoggerFactory.getLogger( DataModelOracleModelDriver.class ); private List<AnnotationDefinition> configuredAnnotations = new ArrayList<AnnotationDefinition>(); private Map<String, AnnotationDriver> annotationDrivers = new HashMap<String, AnnotationDriver>(); private ProjectDataModelOracle oracleDataModel; private ClassLoader projectClassLoader; public static DataModelOracleModelDriver getInstance() { return new DataModelOracleModelDriver(); } public static DataModelOracleModelDriver getInstance(ProjectDataModelOracle oracleDataModel, ClassLoader projectClassLoader) { return new DataModelOracleModelDriver(oracleDataModel, projectClassLoader); } protected DataModelOracleModelDriver( ProjectDataModelOracle oracleDataModel, ClassLoader projectClassLoader ) { this(); this.oracleDataModel = oracleDataModel; this.projectClassLoader = projectClassLoader; } protected DataModelOracleModelDriver() { configuredAnnotations.addAll( CommonAnnotations.getCommonAnnotations() ); for (AnnotationDefinition annotationDefinition : configuredAnnotations) { annotationDrivers.put( annotationDefinition.getClassName(), new DefaultDataModelOracleAnnotationDriver() ); } } @Override public List<AnnotationDefinition> getConfiguredAnnotations() { return configuredAnnotations; } @Override public AnnotationDefinition getConfiguredAnnotation( String annotationClassName ) { for ( AnnotationDefinition annotationDefinition : configuredAnnotations ) { if ( annotationClassName.equals( annotationDefinition.getClassName() ) ) { return annotationDefinition; } } return null; } @Override public AnnotationDriver getAnnotationDriver( String annotationClassName ) { return annotationDrivers.get( annotationClassName ); } @Override public void generateModel( DataModel dataModel, ModelDriverListener generationListener) throws Exception { GenerationContext generationContext = new GenerationContext( dataModel ); generationContext.setGenerationListener( generationListener ); GenerationEngine generationEngine = GenerationEngine.getInstance(); generationEngine.generate( generationContext ); } @Override public DataModel createModel() { return ModelFactoryImpl.getInstance().newModel(); } @Override public ModelDriverResult loadModel( ) throws ModelDriverException { ModelDriverResult result = new ModelDriverResult( ); result.setDataModel( loadModel( oracleDataModel, projectClassLoader ) ); return result; } public DataModel loadModel( ProjectDataModelOracle oracleDataModel, ClassLoader projectClassLoader ) throws ModelDriverException { DataModel dataModel = createModel(); logger.debug( "Adding oracleDataModel: " + oracleDataModel + " to dataModel: " + dataModel ); String[] factTypes = DataModelOracleUtilities.getFactTypes( oracleDataModel ); ObjectSource source = null; if ( factTypes != null && factTypes.length > 0 ) { for ( int i = 0; i < factTypes.length; i++ ) { //skip .drl declared fact types. source = factSource( oracleDataModel, factTypes[ i ] ); if ( source != null && ( ObjectSource.INTERNAL.equals( source ) || ObjectSource.DEPENDENCY.equals( source ) ) ) { addFactType( dataModel, oracleDataModel, factTypes[ i ], source, projectClassLoader ); } } } else { logger.debug( "oracleDataModel hasn't defined fact types" ); } return dataModel; } private void addFactType( DataModel dataModel, ProjectDataModelOracle oracleDataModel, String factType, ObjectSource source, ClassLoader classLoader) throws ModelDriverException { String packageName = NamingUtils.extractPackageName( factType ); String className = NamingUtils.extractClassName( factType ); String superClass = DataModelOracleUtilities.getSuperType( oracleDataModel, factType ); DataObject dataObject; logger.debug( "Adding factType: " + factType + ", to dataModel: " + dataModel + ", from oracleDataModel: " + oracleDataModel ); ClassMetadata classMetadata = readClassMetadata(factType, classLoader); if (classMetadata != null && !classMetadata.isMemberClass() && !classMetadata.isAnonymousClass() && !classMetadata.isLocalClass() ) { Visibility visibility = DriverUtils.buildVisibility( classMetadata.getModifiers() ); dataObject = dataModel.addDataObject( factType, visibility, Modifier.isAbstract( classMetadata.getModifiers() ), Modifier.isFinal( classMetadata.getModifiers() ), source ); dataObject.setSuperClassName( superClass ); //process type annotations Set<Annotation> typeAnnotations = DataModelOracleUtilities.getTypeAnnotations( oracleDataModel, factType ); if ( typeAnnotations != null ) { for ( Annotation annotation : typeAnnotations ) { addFactTypeAnnotation( dataObject, annotation ); } } Map<String, ModelField[]> fields = oracleDataModel.getProjectModelFields(); if ( fields != null ) { ModelField[] factFields = fields.get( factType ); ModelField field; ObjectProperty property; Map<String, Set<Annotation>> typeFieldsAnnotations = DataModelOracleUtilities.getTypeFieldsAnnotations( oracleDataModel, factType ); Set<Annotation> fieldAnnotations; if ( factFields != null && factFields.length > 0 ) { for ( int j = 0; j < factFields.length; j++ ) { field = factFields[ j ]; if ( isLoadableField( field ) ) { if ( field.getType().equals( "Collection" ) ) { //particular processing for collection types //read the correct bag and item classes. String bag = DataModelOracleUtilities.getFieldClassName( oracleDataModel, factType, field.getName() ); String itemsClass = DataModelOracleUtilities.getParametricFieldType( oracleDataModel, factType, field.getName() ); if (itemsClass == null) { //if we don't know the items class, the property will be managed as a simple property. property = dataObject.addProperty( field.getName(), bag ); } else { property = dataObject.addProperty( field.getName(), itemsClass, true, bag ); } } else { property = dataObject.addProperty( field.getName(), getFieldType( oracleDataModel, packageName, field.getClassName() ) ); } //process property annotations if ( typeFieldsAnnotations != null && ( fieldAnnotations = typeFieldsAnnotations.get( field.getName() ) ) != null ) { for ( Annotation fieldAnnotation : fieldAnnotations ) { addFieldAnnotation( dataObject, property, fieldAnnotation ); } } } } } } else { logger.debug( "No fields for factTye: " + factType ); } } } private ClassMetadata readClassMetadata(String factType, ClassLoader classLoader) { try { Class _class = classLoader.loadClass(factType); return new ClassMetadata(_class.getModifiers(), _class.isMemberClass(), _class.isLocalClass(), _class.isAnonymousClass()); } catch (ClassNotFoundException e) { logger.error("It was not possible to read class metadata for class: " + factType); } return null; } private void addFactTypeAnnotation( DataObject dataObject, Annotation annotationToken ) throws ModelDriverException { org.kie.workbench.common.services.datamodeller.core.Annotation annotation = createAnnotation( annotationToken ); if ( annotation != null ) { dataObject.addAnnotation( annotation ); } } private void addFieldAnnotation( DataObject dataObject, ObjectProperty property, Annotation annotationToken ) throws ModelDriverException { org.kie.workbench.common.services.datamodeller.core.Annotation annotation = createAnnotation( annotationToken ); if ( annotation != null ) { property.addAnnotation( annotation ); } } private org.kie.workbench.common.services.datamodeller.core.Annotation createAnnotation( Annotation annotationToken ) throws ModelDriverException { AnnotationDefinition annotationDefinition = getConfiguredAnnotation(annotationToken.getQualifiedTypeName()); org.kie.workbench.common.services.datamodeller.core.Annotation annotation = null; if ( annotationDefinition != null ) { AnnotationDriver annotationDriver = getAnnotationDriver( annotationDefinition.getClassName() ); if ( annotationDriver != null ) { annotation = annotationDriver.buildAnnotation( annotationDefinition, annotationToken ); } else { logger.warn( "AnnotationDriver for annotation: " + annotationToken.getQualifiedTypeName() + " is not configured for this driver" ); } } else { logger.warn( "Annotation: " + annotationToken.getQualifiedTypeName() + " is not configured for this driver." ); } return annotation; } private String getFieldType( ProjectDataModelOracle oracleDataModel, String packageName, String fieldType ) { return fieldType; } /** * True if the given fact type is a DataObject. */ private ObjectSource factSource( ProjectDataModelOracle oracleDataModel, String factType ) { TypeSource oracleType = DataModelOracleUtilities.getTypeSource( oracleDataModel, factType ); // for testing if (factType.startsWith("test")) return ObjectSource.DEPENDENCY; if ( TypeSource.JAVA_PROJECT.equals( oracleType ) ) { return ObjectSource.INTERNAL; } else if ( TypeSource.JAVA_DEPENDENCY.equals( oracleType ) ) { return ObjectSource.DEPENDENCY; } return null; } /** * Indicates if this field should be loaded or not. * Some fields like a filed with name "this" shouldn't be loaded. */ private boolean isLoadableField( ModelField field ) { return ( field.getOrigin().equals( ModelField.FIELD_ORIGIN.DECLARED ) ); } static class OracleGenerationListener implements GenerationListener { org.uberfire.java.nio.file.Path output; IOService ioService; OpenOption option; List<FileChangeDescriptor> fileChanges = new ArrayList<FileChangeDescriptor>(); public OracleGenerationListener( IOService ioService, org.uberfire.java.nio.file.Path output, OpenOption option ) { this.ioService = ioService; this.output = output; this.option = option; } @Override public void assetGenerated( String fileName, String content ) { String subDir; org.uberfire.java.nio.file.Path subDirPath; org.uberfire.java.nio.file.Path destFilePath; StringTokenizer dirNames; subDirPath = output; int index = fileName.lastIndexOf( "/" ); if ( index == 0 ) { //the file names was provided in the form /SomeFile.java fileName = fileName.substring( 1, fileName.length() ); } else if ( index > 0 ) { //the file name was provided in the most common form /dir1/dir2/SomeFile.java String dirNamesPath = fileName.substring( 0, index ); fileName = fileName.substring( index + 1, fileName.length() ); dirNames = new StringTokenizer( dirNamesPath, "/" ); while ( dirNames.hasMoreElements() ) { subDir = dirNames.nextToken(); subDirPath = subDirPath.resolve( subDir ); if ( !ioService.exists( subDirPath ) ) { ioService.createDirectory( subDirPath ); } } } //the last subDirPath is the directory to crate the file. destFilePath = subDirPath.resolve( fileName ); boolean exists = ioService.exists( destFilePath ); ioService.write( destFilePath, content, option ); if ( !exists ) { if ( logger.isDebugEnabled() ) { logger.debug( "Genertion listener created a new file: " + destFilePath ); } fileChanges.add( new FileChangeDescriptor( destFilePath, FileChangeDescriptor.ADD ) ); } else { if ( logger.isDebugEnabled() ) { logger.debug( "Generation listener modified file: " + destFilePath ); } fileChanges.add( new FileChangeDescriptor( destFilePath, FileChangeDescriptor.UPDATE ) ); } } public List<FileChangeDescriptor> getFileChanges() { return fileChanges; } } public class ClassMetadata { int modifiers; boolean memberClass; boolean localClass; boolean anonymousClass; public ClassMetadata(int modifiers, boolean memberClass, boolean localClass, boolean anonymousClass) { this.modifiers = modifiers; this.memberClass = memberClass; this.localClass = localClass; this.anonymousClass = anonymousClass; } public int getModifiers() { return modifiers; } public void setModifiers(int modifiers) { this.modifiers = modifiers; } public boolean isMemberClass() { return memberClass; } public void setMemberClass(boolean memberClass) { this.memberClass = memberClass; } public boolean isLocalClass() { return localClass; } public void setLocalClass(boolean localClass) { this.localClass = localClass; } public boolean isAnonymousClass() { return anonymousClass; } public void setAnonymousClass(boolean anonymousClass) { this.anonymousClass = anonymousClass; } } }