/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ddl.importer; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.emf.ecore.EObject; import org.modeshape.common.text.ParsingException; import org.modeshape.common.text.Position; import org.modeshape.sequencer.ddl.DdlParsers; import org.modeshape.sequencer.ddl.StandardDdlLexicon; import org.modeshape.sequencer.ddl.node.AstNode; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.exception.EmptyArgumentException; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.core.designer.util.FileUtils; import org.teiid.core.designer.util.OperationUtil; import org.teiid.core.designer.util.OperationUtil.Unreliable; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.util.NewModelObjectHelperManager; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.ModelWorkspaceException; import org.teiid.designer.core.xmi.XMIHeader; import org.teiid.designer.ddl.DdlImporterManager; import org.teiid.designer.ddl.DdlNodeImporter; import org.teiid.designer.ddl.TeiidDdlNodeImporter; import org.teiid.designer.ddl.importer.node.EmfModelGenerator; import org.teiid.designer.ddl.importer.node.teiid.MaterializedTableReferenceInfo; import org.teiid.designer.ddl.importer.node.teiid.TeiidDdlImporter; import org.teiid.designer.ddl.registry.DdlNodeImporterRegistry; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.metamodels.relational.BaseTable; import org.teiid.designer.metamodels.relational.Procedure; import org.teiid.designer.metamodels.relational.RelationalPackage; import org.teiid.designer.metamodels.relational.RelationalPlugin; import org.teiid.designer.metamodels.relational.View; import org.teiid.designer.metamodels.relational.extension.RestModelExtensionAssistant; import org.teiid.designer.relational.RelationalConstants; import org.teiid.designer.relational.compare.DifferenceGenerator; import org.teiid.designer.relational.compare.DifferenceReport; import org.teiid.designer.relational.model.RelationalModel; import org.teiid.designer.relational.model.RelationalModelFactory; /** * DdlImporter parses the provided DDL and generates a model containing the parsed entities * * @since 8.0 */ public class DdlImporter { private IProject[] projects; private IContainer modelFolder; private String ddlFileName; private String specifiedParser; private IFile modelFile; private DifferenceReport diffReport; private ModelResource model; private String ddlString; private IStatus importStatus; private boolean noDdlImported; private DdlImporterManager importManager = new DdlImporterManager(); private Set<MaterializedTableReferenceInfo> matTableReferences = new HashSet<MaterializedTableReferenceInfo>(); /** * DdlImporter constructor * @param projects the list of open workspace projects */ public DdlImporter(IProject[] projects ) { this.projects = projects; } /** * @return ddlFileName */ public String ddlFileName() { return ddlFileName; } /** * @param monitor * @param totalWork * @param options * @throws Exception */ public void importDdl(final IProgressMonitor monitor, final int totalWork, final Properties options) throws Exception { OperationUtil.perform(new Unreliable() { private FileReader reader = null; @Override public void doIfFails() { } @Override public void finallyDo() throws Exception { if (reader != null) reader.close(); } @Override public void tryToDo() throws Exception { reader = new FileReader(ddlFileName()); importDdl(reader, monitor, totalWork, options); } }); } void importDdl(FileReader reader, IProgressMonitor monitor, int totalWork, Properties options) throws Exception { ddlString = null; importManager.getImportMessages().clear(); // ------------------------------------------------------------------------------ // Parse the DDL from the file // ------------------------------------------------------------------------------ monitor.subTask(DdlImporterI18n.PARSING_DDL_MSG); // Read the file contents char[] buf = new char[FileUtils.DEFAULT_BUFFER_SIZE]; StringBuilder builder = new StringBuilder(); for (int charTot = reader.read(buf); charTot >= 0; charTot = reader.read(buf)) builder.append(buf, 0, charTot); importDdl(builder.toString(), monitor, totalWork, options); } /** * @param ddl * @param monitor * @param totalWork * @param options * @throws Exception */ public void importDdl(String ddl, IProgressMonitor monitor, int totalWork, Properties options) throws Exception { if( specifiedParser != null && specifiedParser.toUpperCase().equals("TEIID") ) { importTeiidDdl(ddl, monitor, totalWork, options); return; } this.ddlString = ddl; Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); int workUnit = totalWork / 3; // Use specified parser if it has been set AstNode rootNode = null; DdlParsers parsers = new DdlParsers(); if( CoreStringUtil.isEmpty(ddlString) ) { ddlString = "-- No DDL Returned"; //$NON-NLS-1$ this.noDdlImported = true; } try { if(!CoreStringUtil.isEmpty(specifiedParser)) { rootNode = parsers.parseUsing(ddlString,specifiedParser); } else { // No DDL parser is specified - user DdlParsers which will score the best fit rootNode = parsers.parse(ddlString, ddlFileName); } // If parsing exception is encountered, throw DdlImportException } catch (ParsingException e) { String parseMessage = e.getMessage(); importManager.getImportMessages().setParseErrorMessage(parseMessage); Position position = e.getPosition(); importManager.getImportMessages().setHasParseError(true); importManager.getImportMessages().setParseErrorColNumber(position.getColumn()); importManager.getImportMessages().setParseErrorLineNumber(position.getLine()); importManager.getImportMessages().setParseErrorIndex(position.getIndexInContent()); if(!CoreStringUtil.isEmpty(specifiedParser)) { importManager.getImportMessages().setParserId(specifiedParser); } else if(rootNode!=null) { String parserId = (String) rootNode.getProperty(StandardDdlLexicon.PARSER_ID); importManager.getImportMessages().setParserId(parserId); } return; } String parserId = (String) rootNode.getProperty(StandardDdlLexicon.PARSER_ID); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); // ------------------------------------------------------------------------------ // Set up DifferenceProcessor // - startingSelector -- existing model // - endingSelector -- generated from parsed ddl nodes // ------------------------------------------------------------------------------ monitor.subTask(DdlImporterI18n.CREATING_MODEL_MSG); model = ModelerCore.create(modelFile); // Have to specifically set the metamodel type and properties and initialize the containers to anything downstream // has a valid ModelResource model.getModelAnnotation().setPrimaryMetamodelUri( RelationalModelFactory.RELATIONAL_PACKAGE_URI ); model.getModelAnnotation().setModelType(importManager.getModelType()); ModelerCore.getModelEditor().getAllContainers(model.getEmfResource()); model.save(monitor, false); importManager.setRelationalModel(model); RelationalModel targetRelationalModel = importManager.getObjectFactory().createRelationalModel(model); targetRelationalModel.setModelType(importManager.getModelType().getValue()); importManager.setProgressMonitor(monitor); DdlNodeImporter nodeImporter = DdlNodeImporterRegistry.getInstance().getRegistered(parserId.toUpperCase()); if (nodeImporter == null) throw new Exception(DdlImporterPlugin.i18n("noDDLImporterRegisteredMsg", parserId)); //$NON-NLS-1$ importManager.setNodeImporter(nodeImporter); RelationalModel ddlImportModel = nodeImporter.importNode(rootNode,importManager, options); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); // ------------------------------------------------------------------------------ // Generate a DifferenceReport // ------------------------------------------------------------------------------ monitor.subTask(DdlImporterI18n.CREATING_CHANGE_REPORT_MSG); diffReport = DifferenceGenerator.compare(ddlImportModel,targetRelationalModel); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); } private void importTeiidDdl( String ddl, IProgressMonitor monitor, int totalWork, Properties options) throws Exception { this.ddlString = ddl; Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); int workUnit = totalWork / 3; org.teiid.modeshape.sequencer.ddl.node.AstNode rootNode = null; // Use specified parser if it has been set org.teiid.modeshape.sequencer.ddl.DdlParsers parsers = new org.teiid.modeshape.sequencer.ddl.DdlParsers(); if( CoreStringUtil.isEmpty(ddlString) ) { ddlString = "-- No DDL Returned"; //$NON-NLS-1$ this.noDdlImported = true; } try { rootNode = parsers.parseUsing(ddlString,specifiedParser); // If parsing exception is encountered, throw DdlImportException } catch (ParsingException e) { String parseMessage = e.getMessage(); importManager.getImportMessages().setParseErrorMessage(parseMessage); Position position = e.getPosition(); importManager.getImportMessages().setHasParseError(true); importManager.getImportMessages().setParseErrorColNumber(position.getColumn()); importManager.getImportMessages().setParseErrorLineNumber(position.getLine()); importManager.getImportMessages().setParseErrorIndex(position.getIndexInContent()); if(!CoreStringUtil.isEmpty(specifiedParser)) { importManager.getImportMessages().setParserId(specifiedParser); } else if(rootNode!=null) { String parserId = (String) rootNode.getProperty(StandardDdlLexicon.PARSER_ID); importManager.getImportMessages().setParserId(parserId); } return; } String parserId = (String) rootNode.getProperty(StandardDdlLexicon.PARSER_ID); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); // ------------------------------------------------------------------------------ // Set up DifferenceProcessor // - startingSelector -- existing model // - endingSelector -- generated from parsed ddl nodes // ------------------------------------------------------------------------------ monitor.subTask(DdlImporterI18n.CREATING_MODEL_MSG); model = ModelerCore.create(modelFile); // Have to specifically set the metamodel type and properties and initialize the containers to anything downstream // has a valid ModelResource model.getModelAnnotation().setPrimaryMetamodelUri( RelationalModelFactory.RELATIONAL_PACKAGE_URI ); model.getModelAnnotation().setModelType(importManager.getModelType()); ModelerCore.getModelEditor().getAllContainers(model.getEmfResource()); model.save(monitor, false); importManager.setRelationalModel(model); RelationalModel targetRelationalModel = importManager.getObjectFactory().createRelationalModel(model); targetRelationalModel.setModelType(importManager.getModelType().getValue()); importManager.setProgressMonitor(monitor); TeiidDdlNodeImporter nodeImporter = new TeiidDdlImporter(); RelationalModel ddlImportModel = nodeImporter.importNode(rootNode,importManager, options); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); // ------------------------------------------------------------------------------ // Generate a DifferenceReport // ------------------------------------------------------------------------------ monitor.subTask(DdlImporterI18n.CREATING_CHANGE_REPORT_MSG); diffReport = DifferenceGenerator.compare(ddlImportModel,targetRelationalModel); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(workUnit); matTableReferences = ((TeiidDdlImporter)nodeImporter).getMaterializedTableReferences(); } /** * @return the 'true' if has a failure mesage */ public boolean hasParseError() { return importManager.getImportMessages().hasParseError(); } /** * @return the failure message */ public String getParseErrorMessage() { return importManager.getImportMessages().getParseErrorMessage(); } /** * @return the failure error index */ public int getParseErrorIndex() { return importManager.getImportMessages().getParseErrorIndex(); } /** * @return the failure column number */ public int getParseErrorColNumber() { return importManager.getImportMessages().getParseErrorColNumber(); } /** * @return the failure line number */ public int getParseErrorLineNumber() { return importManager.getImportMessages().getParseErrorLineNumber(); } /** * Add a progress message * @param message the progress message */ public void addProgressMessage(String message) { importManager.getImportMessages().addProgressMessage(message); } /** * Get all messages from the import manager * @return list of all messages */ public List<String> getAllMessages() { return importManager.getImportMessages().getAllMessages(); } /** * @return the importStatus */ public IStatus getImportStatus() { return this.importStatus; } /** * @return the last ddl string */ public String getDdlString() { return ddlString; } /** * @return no DDL imported */ public boolean noDdlImported() { return this.noDdlImported; } /** * @return differenceReport */ public DifferenceReport getDifferenceReport() { return diffReport; } /** * @return created model */ public ModelResource model() { return model; } /** * @return modelFile */ public IFile modelFile() { return modelFile; } /** * @return modelFolder */ public IContainer modelFolder() { return modelFolder; } /** * @return modelType */ public ModelType modelType() { return importManager.getModelType(); } /** * @param monitor * @param totalWork * @throws Exception */ public void save(IProgressMonitor monitor, int totalWork ) throws Exception { monitor.subTask(DdlImporterI18n.SAVING_MODEL_MSG); // Set model type etc if new model if (!model.exists()) { ModelAnnotation modelAnnotation = model.getModelAnnotation(); modelAnnotation.setPrimaryMetamodelUri(RelationalPackage.eNS_URI); modelAnnotation.setModelType(importManager.getModelType()); } if( importManager.getModelType() == ModelType.VIRTUAL_LITERAL) { RestModelExtensionAssistant.getRestAssistant().applyMedIfNecessary(model.getCorrespondingResource()); } model.save(new NullProgressMonitor(), true); // Let's save the model, then apply extension properties for relational and rest? // Update the model, based on difference report importStatus = EmfModelGenerator.INSTANCE.execute(diffReport, model, monitor, totalWork); // If Virtual Model, then find all created Tables, Procedures and Views and call help create to get the transformations set correctly if (importManager.getModelType() == ModelType.VIRTUAL_LITERAL) { Properties props = new Properties(); boolean doGenerateDefaultSQL = importManager.optToGenerateDefaultSQL(); boolean doHelpCreateTransform = importManager.optToHelpCreateTransform(); if (doHelpCreateTransform) { if (doGenerateDefaultSQL) { props.put("generateDefaultSQL", doGenerateDefaultSQL); //$NON-NLS-1$ props.put("validate", doGenerateDefaultSQL); //$NON-NLS-1$ } Collection<EObject> targets = new ArrayList<EObject>(); for (Object nextObj : model.getEObjects()) { if (nextObj instanceof Procedure || nextObj instanceof BaseTable || nextObj instanceof View) { try { if( nextObj instanceof BaseTable ) { String value = RelationalPlugin.getExtensionProperty((EObject)nextObj, RelationalConstants.BASE_TABLE_EXT_PROPERTIES.VIEW_TABLE_GLOBAL_TEMP_TABLE); if( value != null && value.equalsIgnoreCase(Boolean.TRUE.toString()) ) { props.put("validate", value); //$NON-NLS-1$ } } NewModelObjectHelperManager.helpCreate(nextObj, props); targets.add((EObject)nextObj); } catch (ModelerCoreException err) { DdlImporterPlugin.UTIL.log(IStatus.ERROR, err, err.getMessage()); } } } } } // Save model model.save(monitor, false); monitor.worked(totalWork); monitor.done(); } /** * @param ddlFileName */ public void setDdlFileName(String ddlFileName ) { this.ddlFileName = null; if (ddlFileName == null) throw new EmptyArgumentException("ddlFileName"); //$NON-NLS-1$ ddlFileName = ddlFileName.trim(); if (ddlFileName.isEmpty()) throw new EmptyArgumentException("ddlFileName"); //$NON-NLS-1$ File file = new File(ddlFileName); if (!file.exists() || file.isDirectory()) throw new IllegalArgumentException(DdlImporterI18n.DDL_FILE_NOT_FOUND_MSG); this.ddlFileName = ddlFileName; } /** * @param modelFolder */ public void setModelFolder(IContainer modelFolder ) { this.modelFolder = modelFolder; } /** * @param modelFolderName */ public void setModelFolder(String modelFolderName ) { modelFolder = null; //chgProcessor = null; if (modelFolderName == null) throw new EmptyArgumentException("modelFolderName"); //$NON-NLS-1$ modelFolderName = modelFolderName.trim(); IPath modelFolderPath = Path.fromPortableString(modelFolderName).makeAbsolute(); if (modelFolderName.isEmpty() || modelFolderPath.segmentCount() == 0) throw new EmptyArgumentException("modelFolderName"); //$NON-NLS-1$ // Verify project is valid String projectName = modelFolderPath.segment(0); IWorkspace workspace = ModelerCore.getWorkspace(); IWorkspaceRoot root = workspace.getRoot(); if (root.findMember(projectName) != null) { boolean found = false; for (IProject project : projects) if (projectName.equals(project.getName())) { found = true; break; } if (!found) throw new IllegalArgumentException(DdlImporterI18n.MODEL_FOLDER_IN_NON_MODEL_PROJECT_MSG); } // Verify folder is valid if (!workspace.validatePath(modelFolderPath.toString(), IResource.PROJECT | IResource.FOLDER).isOK()) throw new IllegalArgumentException( DdlImporterI18n.INVALID_MODEL_FOLDER_MSG); IResource resource = root.findMember(modelFolderPath); // Verify segment in folder is not a file if (resource instanceof IFile) throw new IllegalArgumentException(DdlImporterI18n.MODEL_FOLDER_IS_FILE_MSG); if (resource == null) { if (modelFolderPath.segmentCount() == 1) modelFolder = root.getProject(projectName); else modelFolder = root.getFolder(modelFolderPath); } else modelFolder = (IContainer)resource; // need to re-set model name so model file gets re-generated if( importManager.getModelName() != null ) { setModelName(importManager.getModelName()); } } /** * @param modelName */ public void setModelName(String modelName ) { importManager.setModelName(null); modelFile = null; if (modelName == null) throw new EmptyArgumentException("modelName"); //$NON-NLS-1$ modelName = modelName.trim(); if (modelName.isEmpty()) throw new EmptyArgumentException("modelName"); //$NON-NLS-1$ // Verify name is valid IWorkspace workspace = ModelerCore.getWorkspace(); if (!workspace.validateName(modelName, IResource.FILE).isOK()) throw new IllegalArgumentException( DdlImporterI18n.INVALID_MODEL_NAME_MSG); if (modelFolder != null) { IWorkspaceRoot root = workspace.getRoot(); IPath modelPath = modelFolder.getFullPath().append(modelName); if (!modelName.endsWith(ModelerCore.MODEL_FILE_EXTENSION)) modelPath = modelPath.addFileExtension(ModelerCore.MODEL_FILE_EXTENSION.substring(1)); if (modelFolder.exists()) { // Verify name is not a folder IResource resource = root.findMember(modelPath); if (resource instanceof IContainer) throw new IllegalArgumentException(DdlImporterI18n.MODEL_NAME_IS_FOLDER_MSG); if (resource == null) modelFile = root.getFile(modelPath); else { // Verify name is not a non-model file if (!ModelUtil.isModelFile(resource)) throw new IllegalArgumentException( DdlImporterI18n.MODEL_NAME_IS_NON_MODEL_FILE_MSG); // Verify name is not a non-relational model if (resource.exists()) { XMIHeader xmiHeader = ModelUtil.getXmiHeader(resource); if (xmiHeader == null || !RelationalPackage.eNS_URI.equals(xmiHeader.getPrimaryMetamodelURI())) throw new IllegalArgumentException( DdlImporterI18n.MODEL_NAME_IS_NON_RELATIONAL_MODEL_MSG); } modelFile = (IFile)resource; } } else modelFile = root.getFile(modelPath); } importManager.setModelName(new Path(modelName).removeFileExtension().lastSegment()); } /** * @param modelType Sets modelType to the specified value. */ public void setModelType(ModelType modelType ) { importManager.setModelType(modelType); } /** * @param optToCreateModelEntitiesForUnsupportedDdl */ public void setOptToCreateModelEntitiesForUnsupportedDdl(boolean optToCreateModelEntitiesForUnsupportedDdl ) { importManager.setOptToCreateModelEntitiesForUnsupportedDdl(optToCreateModelEntitiesForUnsupportedDdl); } /** * @param optToSetModelEntityDescription */ public void setOptToSetModelEntityDescription(boolean optToSetModelEntityDescription ) { importManager.setOptToSetModelEntityDescription(optToSetModelEntityDescription); } /** * @param value */ public void setGenerateDefaultSQL(boolean value) { importManager.optToGenerateDefaultSQL(value); } /** * @return the specifiedParser */ public String getSpecifiedParser() { return this.specifiedParser; } /** * Allows a specific parser to be used for the import. If the parser is not specified, * the DdlParsers class is used - which does scoring to determine the best parser * @param specifiedParser the specifiedParser to set */ public void setSpecifiedParser(String specifiedParser) { this.specifiedParser = specifiedParser; } /** * */ public void undoImport() { diffReport = null; } /** * @param modelResources * @throws ModelWorkspaceException */ public void setMaterializedTableReferences(Set<ModelResource> modelResources) throws ModelWorkspaceException { for( MaterializedTableReferenceInfo info : matTableReferences) { // We know the virtual model is this importers model // Need to find the view reference in the info for( final Object eObj : model.getAllRootEObjects()) { if( eObj instanceof BaseTable ) { final BaseTable table = (BaseTable)eObj; if( table.getName().equals(info.getTargetViewName()) ) { BaseTable matTable = getMaterializedTable( info.getMaterializedTableName(), info.getSourceModelName(), modelResources); if( matTable != null ) { table.setMaterializedTable(matTable); } } } } } } private BaseTable getMaterializedTable(String tableName, String modelName, Set<ModelResource> modelResources) throws ModelWorkspaceException { ModelResource sourceModel = getModelForName(modelName, modelResources); if( sourceModel != null ) { return getTableInModel(tableName, sourceModel); } return null; } private ModelResource getModelForName(String name, Set<ModelResource> modelResources) { for( ModelResource mr : modelResources ) { if( mr.getItemName().startsWith(name + ".xmi")) { //$NON-NLS-1$ return mr; } } return null; } private BaseTable getTableInModel(String tableName, ModelResource mr) throws ModelWorkspaceException { for( final Object eObj : mr.getAllRootEObjects()) { if( eObj instanceof BaseTable ) { final BaseTable table = (BaseTable)eObj; if( table.getName().equals(tableName)) { return table; } } } return null; } }