/* * 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.xsd.ui.wizards; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.dialogs.IOverwriteQuery; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages; import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; import org.eclipse.ui.wizards.datatransfer.ImportOperation; import org.eclipse.xsd.XSDImport; import org.eclipse.xsd.XSDSchema; import org.teiid.core.designer.TeiidDesignerException; import org.teiid.designer.common.xsd.XsdHeader; import org.teiid.designer.common.xsd.XsdHeaderReader; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.util.URLHelper; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.ModelWorkspaceManager; import org.teiid.designer.ui.common.viewsupport.JobUtils; import org.teiid.designer.ui.common.widget.ListMessageDialog; import org.teiid.designer.ui.viewsupport.ModelUtilities; import org.teiid.designer.xsd.ui.ModelerXsdUiConstants; /** * @since 8.0 */ public class XsdFileSystemImportUtil { private static final String I18N_PREFIX = "XsdFileSystemImportUtil"; //$NON-NLS-1$ private static final String SEPARATOR = "."; //$NON-NLS-1$ private static final String FILE_SEPARATOR = File.separator; /** * Import XSD schemas from the given files. * * @param xsdFiles the given XSD files * @param addDependentXsds whether to import dependent XSD schemas * @param destinationFullPath the full path in the workspace where to import the XSDs * @param container the container that contains the wizard page * @param overwriteQuery implementation of IOverwriteQuery * @param createContainerStructure whether to create container structure * @param overwriteExistingResources whether to overwite existing resources * @return * @since 5.5 */ public static boolean importXsds( List xsdFiles, boolean addDependentXsds, IPath destinationFullPath, IWizardContainer container, IOverwriteQuery overwriteQuery, boolean createContainerStructure, boolean overwriteExistingResources ) { return importXsds(xsdFiles, addDependentXsds, destinationFullPath, container, overwriteQuery, createContainerStructure, overwriteExistingResources, Collections.EMPTY_MAP); } /** * Import XSD schemas from the given files. * * @param xsdFiles the given XSD files * @param addDependentXsds whether to import dependent XSD schemas * @param destinationFullPath the full path in the workspace where to import the XSDs * @param container the container that contains the wizard page * @param overwriteQuery implementation of IOverwriteQuery * @param createContainerStructure whether to create container structure * @param overwriteExistingResources whether to overwite existing resources * @param Map fileToUserInfo optional property for each file to be imported. This is useful for the XSD that has dependent * XSDs with URL (HTTP/HTTPS) locations * @return * @since 5.5 */ public static boolean importXsds( List xsdFiles, boolean addDependentXsds, IPath destinationFullPath, IWizardContainer container, IOverwriteQuery overwriteQuery, boolean createContainerStructure, boolean overwriteExistingResources, Map fileToUserInfo ) { Shell shell = container.getShell(); if (xsdFiles.size() > 0) { if (addDependentXsds) { // we need to get the dependent models (Files) and add those too!!! Map depFilesImportToPaths = new HashMap(); List depFiles = new ArrayList(getDependentXsdFiles(xsdFiles, depFilesImportToPaths, fileToUserInfo)); boolean OK_TO_CONTINUE = false; if (depFiles.isEmpty()) { OK_TO_CONTINUE = true; } else { OK_TO_CONTINUE = ListMessageDialog.openQuestion(shell, getString("addingDependentXsds.title"), //$NON-NLS-1$ null, getString("addingDependentXsds.message"), //$NON-NLS-1$ new ArrayList(depFilesImportToPaths.keySet()), null); } if (OK_TO_CONTINUE) { List allFiles = new ArrayList(xsdFiles.size() + depFiles.size()); allFiles.addAll(xsdFiles); allFiles.addAll(depFiles); // Now we need to insure that the folder structure is complete for all xsd's // Note that we'll have to find the top-most parent to make this happen? File topLevelSourceDirectory = getTopLevelCommonFolder(allFiles); addMissingFoldersToFileList(topLevelSourceDirectory, allFiles); return importResources(allFiles, container, overwriteQuery, topLevelSourceDirectory, destinationFullPath, depFilesImportToPaths, createContainerStructure, overwriteExistingResources); } return false; } File topLevelSourceDirectory = getTopLevelCommonFolder(xsdFiles); return importResources(xsdFiles, container, overwriteQuery, topLevelSourceDirectory, destinationFullPath, null, createContainerStructure, overwriteExistingResources); } MessageDialog.openInformation(shell, DataTransferMessages.DataTransfer_information, DataTransferMessages.FileImport_noneSelected); return false; } private static Collection getDependentXsdFiles( Collection selectedFiles, Map depFilesImportToPaths, Map fileToUserInfo ) { Collection modifiableList = new ArrayList(); for (Iterator iter = selectedFiles.iterator(); iter.hasNext();) { addDependentXsdFiles((File)iter.next(), modifiableList, selectedFiles, depFilesImportToPaths, fileToUserInfo); } return modifiableList; } private static void addDependentXsdFiles( File xsdFile, Collection modifiableList, Collection originalList, Map depFilesImportToPaths, Map fileToUserInfo ) { XsdHeader header = null; try { header = XsdHeaderReader.readHeader(xsdFile); } catch (TeiidDesignerException theException) { ModelerXsdUiConstants.Util.log(theException); } if (header != null) { // Add all the imported schema locations String[] locations = header.getImportSchemaLocations(); addDepFilesForLocations(locations, xsdFile, modifiableList, originalList, depFilesImportToPaths, fileToUserInfo); // Add all the included schema locations locations = header.getIncludeSchemaLocations(); addDepFilesForLocations(locations, xsdFile, modifiableList, originalList, depFilesImportToPaths, fileToUserInfo); } } private static void addDepFilesForLocations( String[] locations, File xsdFile, Collection modifiableList, Collection originalList, Map depFilesImportToPaths, Map fileToUserInfo ) { for (int i = 0; i != locations.length; ++i) { final String location = locations[i]; String absolutePath = getAbsolutePath(xsdFile, location); File f = new File(absolutePath); if (f.exists() && !listContainsFileByPath(modifiableList, f) && !listContainsFileByPath(originalList, f)) { // Add to list of dependent XSDs modifiableList.add(f); depFilesImportToPaths.put(location, absolutePath); addDependentXsdFiles(f, modifiableList, originalList, depFilesImportToPaths, fileToUserInfo); } else { // check if it is url try { URL url = new URL(absolutePath); String filePath = url.getPath(); if (filePath.startsWith("/")) {//$NON-NLS-1$ filePath = filePath.substring(1); } String userName = null; String password = null; boolean verifyHostname = true; Object userInfo[] = (Object[])fileToUserInfo.get(xsdFile); if (userInfo != null) { userName = (String)userInfo[0]; password = (String)userInfo[1]; verifyHostname = ((Boolean)userInfo[2]).booleanValue(); } f = URLHelper.createFileFromUrl(url, filePath, userName, password, verifyHostname); // Add to list of dependent XSDs modifiableList.add(f); depFilesImportToPaths.put(location, f.getAbsolutePath()); addDependentXsdFiles(f, modifiableList, originalList, depFilesImportToPaths, fileToUserInfo); } catch (MalformedURLException e) { // not URL } catch (IOException ioe) { ModelerXsdUiConstants.Util.log(ioe); } } } } /** * This method returns a File (i.e. Folder) which represents the top-most common folder required to contain the full * path/directory structure for the xsd schema files contained in the input allFiles Collection. * * @param allFiles * @return * @since 5.5 */ private static File getTopLevelCommonFolder( Collection allFiles ) { // We need to walk through each file's segment // If we find one segment (i.e. folder) that's different or if there's no more folders (i.e. at the file) // then we've found the root. If it is different than the the "location" selected by the user, we need to change this // somehow. // Let's break these up into Tokens String nextSegment = null; File nextFile = null; int minSegCount = 99; Collection segmentsList = new ArrayList(); int minPathId = 0; for (Iterator iter = allFiles.iterator(); iter.hasNext();) { nextFile = (File)iter.next(); Collection segments = new ArrayList(); for (final StringTokenizer textIter = new StringTokenizer(nextFile.getAbsolutePath(), FILE_SEPARATOR); textIter.hasMoreTokens();) { nextSegment = textIter.nextToken().trim(); segments.add(nextSegment); } segmentsList.add(segments.toArray()); if (segments.size() < minSegCount) { minSegCount = segments.size(); minPathId = segmentsList.size() - 1; } } // Now that we have them broken up in segments, and have a minSegCount Object[] segmentsArray = segmentsList.toArray(); Object[] pathArray = null; Object[] minPathArray = (Object[])segmentsArray[minPathId]; for (int j = 0; j < minSegCount; j++) { String baseSeg = (String)minPathArray[j]; String compareSeg = null; for (int i = 1; i < segmentsArray.length; i++) { pathArray = (Object[])segmentsArray[i]; compareSeg = (String)pathArray[j]; if (!baseSeg.equalsIgnoreCase(compareSeg)) { // We've found a path that is different, so we bail // and get the segments prior to this, and String topLevelFolder = null; for (int k = 0; k < j - 1; k++) { if (k == 0) topLevelFolder = minPathArray[k] + FILE_SEPARATOR; else topLevelFolder = topLevelFolder + minPathArray[k] + FILE_SEPARATOR; } topLevelFolder = topLevelFolder + minPathArray[j - 1]; return new File(topLevelFolder); } } } String topLevelFolder = null; for (int k = 0; k < minPathArray.length - 1; k++) { if (k == 0) topLevelFolder = minPathArray[k] + FILE_SEPARATOR; else topLevelFolder = topLevelFolder + minPathArray[k] + FILE_SEPARATOR; } if (minPathArray.length > 2) { topLevelFolder = topLevelFolder + minPathArray[minPathArray.length - 1]; } return new File(topLevelFolder); } /* * This method needs to create/add any folders required that between each xsd file and the topLevelFolder */ private static void addMissingFoldersToFileList( File topLevelFolder, Collection allFiles ) { // For each xsd, get it's path/name // walk backwards from the modelName and create a folder at each level up to the topLevelFolder File nextFile = null; String nextSegment; // Tokenize the top level segment Collection topLevelSegments = new ArrayList(); for (final StringTokenizer textIter = new StringTokenizer(topLevelFolder.getAbsolutePath(), FILE_SEPARATOR); textIter.hasMoreTokens();) { nextSegment = textIter.nextToken().trim(); topLevelSegments.add(nextSegment); } Object[] topSegArray = topLevelSegments.toArray(); int nBaseSegs = topSegArray.length; Collection missingFolders = new ArrayList(); for (Iterator iter = allFiles.iterator(); iter.hasNext();) { nextFile = (File)iter.next(); Collection segments = new ArrayList(); for (final StringTokenizer textIter = new StringTokenizer(nextFile.getAbsolutePath(), FILE_SEPARATOR); textIter.hasMoreTokens();) { nextSegment = textIter.nextToken().trim(); segments.add(nextSegment); } // if the segment count for a file is greater than the segment count for the top level folder // then we need to create folders up to nSegs-1 String thisPath = topLevelFolder.getAbsolutePath(); File existingFolder = null; if (segments.size() > nBaseSegs + 1) { Object[] thisSegArray = segments.toArray(); // Now that we have both the base array and this array for (int i = nBaseSegs; i < thisSegArray.length - 1; i++) { thisPath = thisPath + FILE_SEPARATOR + thisSegArray[i]; } existingFolder = new File(thisPath); if (!existingFolder.exists() && !listContainsFileByPath(missingFolders, existingFolder)) { // System.out.println(" ADDING MISSING FOLDER = " + existingFolder); missingFolders.add(existingFolder); } } } if (!missingFolders.isEmpty()) { Collection files = new ArrayList(allFiles); allFiles.clear(); allFiles.addAll(missingFolders); allFiles.addAll(files); } } private static boolean listContainsFileByPath( Collection files, File file ) { for (Iterator iter = files.iterator(); iter.hasNext();) { File nextFile = (File)iter.next(); if (nextFile.getAbsolutePath().equals(file.getAbsolutePath())) { return true; } } return false; } private static String getAbsolutePath( final File base, final String relativePath ) { URI baseLocation = URI.createFileURI(base.getAbsolutePath()); URI relLocation = URI.createURI(relativePath, false); if (baseLocation.isHierarchical() && !baseLocation.isRelative() && relLocation.isRelative()) { relLocation = relLocation.resolve(baseLocation); } if (relLocation.isFile()) { return relLocation.toFileString(); } return URI.decode(relLocation.toString()); } /** * Import the XSD resources into the workspace. * * @param fileSystemObjects * @param container * @param overwriteQuery * @param topLevelSourceDirectory * @param destinationFullPath * @param depFilesImportToPaths * @param createContainerStructure * @param overwriteExistingResources * @return * @since 5.5 */ public static boolean importResources( List fileSystemObjects, IWizardContainer container, IOverwriteQuery overwriteQuery, File topLevelSourceDirectory, IPath destinationFullPath, Map depFilesImportToPaths, boolean createContainerStructure, boolean overwriteExistingResources ) { ImportOperation operation = new ImportOperation(destinationFullPath, topLevelSourceDirectory, FileSystemStructureProvider.INSTANCE, overwriteQuery, fileSystemObjects); operation.setContext(container.getShell()); // Let's cache the auto-build and reset after. boolean autoBuildOn = ModelerCore.getWorkspace().isAutoBuilding(); if (autoBuildOn) { JobUtils.setAutoBuild(false); } operation.setCreateContainerStructure(createContainerStructure); operation.setOverwriteResources(overwriteExistingResources); boolean importResult = executeImportOperation(operation, container); // We need to reset these files to "Not Modified" Collection emfResources = new HashSet(); for (Iterator iter = fileSystemObjects.iterator(); iter.hasNext();) { Object nextObj = iter.next(); if (nextObj instanceof File && !((File)nextObj).isDirectory()) { // ------------------------------------------------------ // Defect 24934 - Need to cache the target XSD File, so we can create the proper "Import" statement for each // dependent xsd file // Must use original source SCHEMA files ... NOT ModelResource file paths File targetXSDFile = (File)nextObj; String name = targetXSDFile.getName(); // do something here if .xsd if (name.indexOf(".xsd") > -1) { //$NON-NLS-1$ Resource[] resources = null; try { resources = ModelWorkspaceManager.getModelWorkspaceManager().getModelContainer().getResourceFinder().findByName(name, false, false); if (resources != null && resources.length == 1) { ModelResource mr = ModelUtilities.getModelResource(resources[0], true); if (mr != null) { emfResources.add(mr.getEmfResource()); } // reset import locations to relative paths if (depFilesImportToPaths != null && !depFilesImportToPaths.isEmpty() && resources[0].getContents().size() == 1) { Object content = resources[0].getContents().get(0); if (content instanceof XSDSchema) { boolean importChanged = false; Iterator iter1 = ((XSDSchema)content).getContents().iterator(); while (iter1.hasNext()) { content = iter1.next(); if (content instanceof XSDImport) { XSDImport xsdImport = ((XSDImport)content); String xsdLocation = xsdImport.getSchemaLocation(); String absoluteImportLocation = (String)depFilesImportToPaths.get(xsdLocation); // ------------------------------------------------------ // Defect 24934 - ON LINUX, the getAbsolutePath() returns a path with a duplicate // folder path if there is a // symbolic link in the path name. // Fix is to use getPath() instead. String topLevelDirectoryPath = topLevelSourceDirectory.getPath(); if (!Platform.getOS().equals(Platform.WS_WIN32)) { if (topLevelDirectoryPath.charAt(0) != '/') { topLevelDirectoryPath = '/' + topLevelDirectoryPath; } } // ------------------------------------------------------ // Defect 24934 - Use the target XSD file for the base path (instead of XSD Model // Resource) IPath projectRelativeBaseXSDFilePath = new Path( targetXSDFile.getPath().substring(topLevelDirectoryPath.length() + 1)); if (absoluteImportLocation != null) { // Get the IPath projectRelativeImportedXsdFilePath = new Path( absoluteImportLocation.substring(topLevelDirectoryPath.length() + 1)); String relativeXsdLocation = ModelUtil.getRelativePath(projectRelativeImportedXsdFilePath, projectRelativeBaseXSDFilePath); if (!xsdLocation.equals(relativeXsdLocation)) { xsdImport.setSchemaLocation(relativeXsdLocation); importChanged = true; } } } } if (importChanged) { mr.save(null, false); } } } } } catch (CoreException theException) { } } } } Iterator iter = emfResources.iterator(); while (iter.hasNext()) { ((Resource)iter.next()).setModified(false); } if (autoBuildOn) { JobUtils.setAutoBuild(true); } return importResult; } /** * Execute the passed import operation. Answer a boolean indicating success. */ private static boolean executeImportOperation( ImportOperation op, IWizardContainer container ) { try { container.run(true, true, op); } catch (InterruptedException e) { return false; } catch (InvocationTargetException e) { Throwable exception = e.getTargetException(); String message = exception.getMessage(); // Some system exceptions have no message if (message == null) message = NLS.bind(IDEWorkbenchMessages.WizardDataTransfer_exceptionMessage, exception); MessageDialog.openError(container.getShell(), IDEWorkbenchMessages.WizardExportPage_internalErrorTitle, message); return false; } IStatus status = op.getStatus(); if (!status.isOK()) { ErrorDialog.openError(container.getShell(), DataTransferMessages.FileImport_importProblems, null, // no special // message status); return false; } return true; } private static String getString( final String id ) { return ModelerXsdUiConstants.Util.getString(I18N_PREFIX + SEPARATOR + id); } }