//------------------------------------------------------------------------------ // Copyright (c) 2005, 2007 IBM Corporation and others. // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // Contributors: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.library.xmi; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.epf.common.service.versioning.VersionUtil; import org.eclipse.epf.common.serviceability.DebugTrace; import org.eclipse.epf.library.AbstractLibraryManager; import org.eclipse.epf.library.ILibraryResourceManager; import org.eclipse.epf.library.LibraryAlreadyExistsException; import org.eclipse.epf.library.LibraryNotFoundException; import org.eclipse.epf.library.LibraryResources; import org.eclipse.epf.library.LibraryServiceException; import org.eclipse.epf.library.edit.meta.TypeDefUtil; import org.eclipse.epf.library.edit.util.DebugUtil; import org.eclipse.epf.library.edit.util.ProcessUtil; import org.eclipse.epf.library.layout.LayoutResources; import org.eclipse.epf.library.persistence.ILibraryResourceSet; import org.eclipse.epf.library.persistence.PersistenceService; import org.eclipse.epf.library.project.MethodLibraryProject; import org.eclipse.epf.library.util.ModelStorage; import org.eclipse.epf.persistence.MultiFileResourceSetImpl; import org.eclipse.epf.persistence.MultiFileSaveUtil; import org.eclipse.epf.persistence.migration.MappingUtil; import org.eclipse.epf.persistence.util.PersistenceUtil; import org.eclipse.epf.services.Services; import org.eclipse.epf.uma.MethodLibrary; import org.eclipse.epf.uma.util.ExtendedOpposite; import org.eclipse.epf.uma.util.ExtendedReference; import org.eclipse.epf.uma.util.ModifiedTypeMeta; import org.eclipse.epf.uma.util.UserDefinedTypeMeta; import org.eclipse.osgi.util.NLS; import com.ibm.icu.util.Calendar; /** * The default XMI Library Manager implementation. * * @author Kelvin Low * @author Jinhua Xi * @author Phong Nguyen Le * * @since 1.0 */ public class XMILibraryManager extends AbstractLibraryManager { /** * The supported library type. */ public static final String LIBRARY_TYPE = Services.XMI_PERSISTENCE_TYPE; /** * The library XMI file name. */ public static final String LIBRARY_XMI = MultiFileSaveUtil.DEFAULT_LIBRARY_MODEL_FILENAME; /** * The plugin and config spec export file name. */ public static final String exportFile = MultiFileSaveUtil.DEFAULT_PLUGIN_EXPORT_FILENAME; /** * The library path. */ public static final String ARG_LIBRARY_PATH = "library.path"; //$NON-NLS-1$ // The absolute path to the managed library. protected String path; private ILibraryResourceManager resourceMgr; private IProject project; private String registerType = "Default"; //$NON-NLS-1$ public XMILibraryManager() { super(); resourceMgr = new XMILibraryResourceManager(); } protected boolean isReopeningLib = false; protected boolean isReopeningSynLib = false; /** * Creates a new method library. * * @param name * a name for the new method library * @param args * method library specific arguments * @return a method library * @throw <code>LibraryServiceException</code> if an error occurs while * performing the operation */ public MethodLibrary createMethodLibrary(final String name, final Map<String, Object> args) throws LibraryServiceException { final MethodLibrary[] resultHolder = new MethodLibrary[1]; final LibraryServiceException[] exceptionHolder = new LibraryServiceException[1]; IWorkspaceRunnable action = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { try { resultHolder[0] = doCreateMethodLibrary(name, args); } catch(LibraryServiceException e) { exceptionHolder[0] = e; } } }; try { IWorkspace workspace = ResourcesPlugin.getWorkspace(); workspace.run(action, workspace.getRuleFactory().createRule(workspace.getRoot()), IWorkspace.AVOID_UPDATE, null); } catch (CoreException e) { XMILibraryPlugin.getDefault().getLogger().logError(e); throw new LibraryServiceException(e); } if(exceptionHolder[0] != null) { throw exceptionHolder[0]; } return resultHolder[0]; } private MethodLibrary doCreateMethodLibrary(String name, Map<String, Object> args) throws LibraryServiceException { if (debug) { DebugTrace.print(this, "createMethodLibrary", "name=" + name); //$NON-NLS-1$ //$NON-NLS-2$ } if (name == null || name.length() == 0 || args == null) { throw new IllegalArgumentException(); } String path = (String) args.get(ARG_LIBRARY_PATH); if (path == null || path.length() == 0) { throw new IllegalArgumentException(); } File libraryPath = new File(path); File libraryXMIFile = new File(libraryPath, LIBRARY_XMI); if (libraryXMIFile.exists()) { String msg = NLS.bind( XMILibraryResources.libraryAlreadyExistsError_msg, libraryPath.getAbsolutePath()); throw new LibraryAlreadyExistsException(msg); } if (!libraryPath.exists()) { libraryPath.mkdirs(); } try { skipEventProcessing = true; String regType = (String) args.get(ARG_LIBRARY_REGISTER_TYPE); if (regType != null && regType.equals("ConfigExport")) { //$NON-NLS-1$ String time = Long.toHexString(Calendar.getInstance().getTimeInMillis()); MethodLibraryProject.openProject(libraryPath.getAbsolutePath(), "ExportLib" + time, //$NON-NLS-1$ null); } else { // Open the method library project file. project = MethodLibraryProject.openProject(libraryPath.getAbsolutePath(), null); } // Create the resource set. ILibraryResourceSet resourceSet = (ILibraryResourceSet) editingDomain .getResourceSet(); // Create a new method library. ModelStorage.newLibrary(resourceSet, name, libraryPath .getAbsolutePath(), true); library = resourceSet.getFirstMethodLibrary(); // Add a listener to monitor library resource changes. addResourceChangedListeners(); if (debug) { DebugTrace.print(this, "createMethodLibrary", "library=" + library); //$NON-NLS-1$ //$NON-NLS-2$ } return library; } catch (Exception e) { XMILibraryPlugin.getDefault().getLogger().logError(e); throw new LibraryServiceException(e); } finally { skipEventProcessing = false; // // event processed in LibraryService // notifyListeners(ILibraryChangeListener.OPTION_CREATED, null); } } /** * Opens a method library. * * @param uri * a method library URI * @return a method library * @throw <code>LibraryServiceException</code> if an error occurs while * performing the operation */ public MethodLibrary openMethodLibrary(java.net.URI uri) throws LibraryServiceException { if (debug) { DebugTrace.print(this, "openMethodLibrary"); //$NON-NLS-1$ } if (uri == null) { throw new IllegalArgumentException(); } try { File file = new File(uri); library = openMethodLibrary(file); } catch(LibraryServiceException e) { throw e; } catch (Exception e) { library = null; } if (debug) { DebugTrace.print(this, "openMethodLibrary", "library=" + library); //$NON-NLS-1$ //$NON-NLS-2$ } return library; } /** * Opens a method library. * * @param path * a <code>File</code> object that contains the path to the * method library. * @return a <code>MethodLibrary</code>. * @throw <code>LibraryServiceException</code> if an error occurred while * performing the operation. */ protected MethodLibrary openMethodLibrary(final File path) throws LibraryServiceException { final MethodLibrary[] libHolder = new MethodLibrary[1]; final LibraryServiceException[] exceptionHolder = new LibraryServiceException[1]; if (ResourcesPlugin.getWorkspace().isTreeLocked()) { // if user opens a project from Navigator view, the // workspace-runable cannot be run successfully because the // workspace is locked at that time and an exception will be // thrown. // TODO: this work-around however will not defer the notification // of resource change events until after the library is opened. // This might cause concurrent modification exception. // try { libHolder[0] = doOpenMethodLibrary(path); } catch (LibraryServiceException e) { exceptionHolder[0] = e; } } else { IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { try { libHolder[0] = doOpenMethodLibrary(path); } catch (LibraryServiceException e) { exceptionHolder[0] = e; } } }; try { ResourcesPlugin.getWorkspace().run(workspaceRunnable, ResourcesPlugin.getWorkspace().getRoot(), IWorkspace.AVOID_UPDATE, new NullProgressMonitor()); } catch (CoreException e) { throw new LibraryServiceException(e); } } if(exceptionHolder[0] != null) { throw exceptionHolder[0]; } return libHolder[0]; } protected MethodLibrary doOpenMethodLibrary(File path) throws LibraryServiceException { File libraryXMIFile = new File(path, LIBRARY_XMI); if (!libraryXMIFile.exists()) { throw new LibraryNotFoundException(); } VersionUtil.VersionCheckInfo info = VersionUtil.checkLibraryVersion(libraryXMIFile); IStatus status = XMILibraryUtil.checkVersion(libraryXMIFile, info); if(!status.isOK()) { throw new LibraryServiceException(status.getMessage()); } else if(MappingUtil.conversionRequired(libraryXMIFile.getAbsolutePath(), info)) { throw new LibraryServiceException(LibraryResources.libUpgradeRequired_err_msg); } try { skipEventProcessing = true; // Open the method library project file. project = MethodLibraryProject.openProject(path.getAbsolutePath(), null); // Create the resource set. ILibraryResourceSet resourceSet = ((ILibraryResourceSet) editingDomain .getResourceSet()); // Load the method library. resourceSet.loadMethodLibraries(URI.createFileURI(libraryXMIFile .getAbsolutePath()), Collections.EMPTY_MAP); library = resourceSet.getFirstMethodLibrary(); // Add a listener to monitor library resource changes. addResourceChangedListeners(); System.gc(); return library; } catch (Exception e) { if (debug) { DebugTrace.print(e); } throw new LibraryServiceException(e); } finally { firePropertyChange(library, PROP_DIRTY); skipEventProcessing = false; } } /** * Opens a method library. * * @param args * method library specific arguments * @return a method library * @throw <code>LibraryServiceException</code> if an error occurs while * performing the operation */ public MethodLibrary openMethodLibrary(Map args) throws LibraryServiceException { if (debug) { DebugTrace.print(this, "openMethodLibrary"); //$NON-NLS-1$ } if (args == null) { throw new IllegalArgumentException(); } String path = (String) args.get(ARG_LIBRARY_PATH); if (path == null || path.length() == 0) { throw new IllegalArgumentException(); } library = openMethodLibrary(new File(path)); if (debug) { DebugTrace.print(this, "openMethodLibrary", "library=" + library); //$NON-NLS-1$ //$NON-NLS-2$ } return library; } /** * Reopens the managed method library. * * @return a method library * @throw <code>LibraryServiceException</code> if an error occurs while * performing the operation */ public MethodLibrary reopenMethodLibrary() throws LibraryServiceException { if (debug) { DebugTrace.print(this, "reopenMethodLibrary"); //$NON-NLS-1$ } try { isReopeningLib = true; if (library != null && ProcessUtil.isSynFree()) { isReopeningSynLib = true; } library = openMethodLibrary(new File(getMethodLibraryLocation())); } finally { isReopeningLib = false; isReopeningSynLib = false; } if (debug) { DebugTrace.print(this, "reopenMethodLibrary", "library=" + library); //$NON-NLS-1$ //$NON-NLS-2$ } return library; } /* * (non-Javadoc) * * @see org.eclipse.epf.library.AbstractLibraryManager#getLibraryPersisterType() */ protected String getLibraryPersisterType() { return Services.XMI_PERSISTENCE_TYPE; } /* * (non-Javadoc) * * @see org.eclipse.epf.library.AbstractLibraryManager#createResourceSet() */ protected ILibraryResourceSet createResourceSet() { return PersistenceService.INSTANCE .createResourceSet(Services.XMI_PERSISTENCE_TYPE); } /** * Gets the absolute path to the managed method library. * For distributed library, this is the library's workspace path. * * @return an absolute path to the method library */ public String getMethodLibraryLocation() { if (debug) { DebugTrace.print(this, "getMethodLibraryPath"); //$NON-NLS-1$ } java.net.URI libraryURI = getMethodLibraryURI(); if (libraryURI != null) { File libraryXMIFile = new File(libraryURI); if (libraryXMIFile.getName().equalsIgnoreCase(LIBRARY_XMI)) { libraryXMIFile = libraryXMIFile.getParentFile(); } return libraryXMIFile.getAbsolutePath(); } return null; } /** * Gets the project of the method library managed by this manager. * * @return the method library project */ public IProject getMethodLibraryProject() { return project; } public void handleLibraryMoved() { if(library == null) { return; } String location = getMethodLibraryLocation(); if(!project.isOpen() || project.getLocation().equals(new Path(location))) { // not moved // return; } // update URI of all library resources // Resource libResource = library.eResource(); if(libResource == null || libResource.getResourceSet() == null) { return; } String newLocation = project.getLocation().toOSString(); if(libResource.getResourceSet() instanceof MultiFileResourceSetImpl) { ((MultiFileResourceSetImpl)libResource.getResourceSet()).handleLibraryMoved(newLocation); } else { PersistenceUtil.replaceURIPrefix(new ArrayList<Resource>(libResource.getResourceSet().getResources()), location, newLocation); } } /** * get the resource manager for the library * @return ILibraryResourceManager */ public ILibraryResourceManager getResourceManager() { return resourceMgr; } /* (non-Javadoc) * @see org.eclipse.epf.library.ILibraryManager#backupMethodLibrary(java.lang.String) */ public void backupMethodLibrary(String path) { String libPathStr = getMethodLibraryLocation(); File libPath = new File(libPathStr); // excude the non-library files that might be locked by rmc. // these files may cause backup to fail due to file lock. String excludes = ".lock"; //$NON-NLS-1$ LayoutResources.copyDir(libPath, new File(path), "**", excludes); //$NON-NLS-1$ } private String getRegisterType() { return registerType; } private void setRegisterType(String registerType) { this.registerType = registerType; } /* (non-Javadoc) * @see org.eclipse.epf.library.ILibraryManager#registerMethodLibrary(org.eclipse.epf.uma.MethodLibrary, java.util.Map) */ public void registerMethodLibrary(MethodLibrary lib, Map<String, Object> params) throws LibraryServiceException { String regType = (String) params.get(ARG_LIBRARY_REGISTER_TYPE); if (regType != null) { setRegisterType(regType); } regType = getRegisterType(); if (regType.equals("ConfigExport")) {//$NON-NLS-1$ //For now, still simply wraping up old implementation (copied from ConfigurationExportService) for smoother change; will definetly change later //so that creating a new lib would not be needed String name = "library.xmi"; //$NON-NLS-1$; createMethodLibrary(name, params); setMethodLibrary(lib); return; } //Minimum implementation for defaul register type, to minimize caller code change this.library = lib; } /* (non-Javadoc) * @see org.eclipse.epf.library.ILibraryManager#unRegisterMethodLibrary() */ public void unRegisterMethodLibrary() throws LibraryServiceException { if (getRegisterType().equals("ConfigExport")) {//$NON-NLS-1$ closeMethodLibrary(); return; } //Minimum implementation for defaul register type, to minimize caller code change this.library = null; } private Map<String, UserDefinedTypeMeta> userDefinedTypeMap; public Collection<UserDefinedTypeMeta> getUserDefinedTypes() { return userDefinedTypeMap == null ? null : userDefinedTypeMap.values(); } public void addUserDefineType(UserDefinedTypeMeta meta) { if (userDefinedTypeMap == null) { userDefinedTypeMap = new HashMap<String, UserDefinedTypeMeta>(); } userDefinedTypeMap.put(meta.getId(), meta); } public UserDefinedTypeMeta getUserDefineType(String id) { return userDefinedTypeMap == null ? null : userDefinedTypeMap.get(id); } private boolean userDefinedTypeLoaded = false; public boolean isUserDefinedTypeLoaded() { return userDefinedTypeLoaded; } public void setUserDefinedTypeLoaded(boolean b) { if (DebugUtil.udtDebug && !userDefinedTypeLoaded && b) { String str = TypeDefUtil.getMTypesDebugString(getModifiedTypes(), "User defined types loaded");//$NON-NLS-1$ DebugUtil.print(str); } userDefinedTypeLoaded = b; } public void prepareToLoadUserDefinedTypes() { userDefinedTypeMap = null; modifiedTypeMap = null; } private Map<String, ModifiedTypeMeta> modifiedTypeMap; public Collection<ModifiedTypeMeta> getModifiedTypes() { return modifiedTypeMap == null ? null : modifiedTypeMap.values(); } public void addModifiedType(ModifiedTypeMeta meta) { if (modifiedTypeMap == null) { modifiedTypeMap = new HashMap<String, ModifiedTypeMeta>(); } modifiedTypeMap.put(meta.getId(), meta); } private ModifiedTypeMeta noneValue = (ModifiedTypeMeta) TypeDefUtil .getInstance().createMetaDef(ModifiedTypeMeta.class); public ModifiedTypeMeta getModifiedType(String id) { if (modifiedTypeMap == null) { return null; } ModifiedTypeMeta meta = modifiedTypeMap.get(id); if (meta == noneValue) { return null; } else if (meta != null) { return meta; } try { Class cls = Class.forName(id); if (cls == null) { modifiedTypeMap.put(id, noneValue); return null; } List<Class> list = new ArrayList<Class>(); list.add(cls); cls = TypeDefUtil.getSuperClass(cls); while (cls != null) { meta = modifiedTypeMap.get(cls.getName()); if (meta != null) { break; } list.add(cls); cls = TypeDefUtil.getSuperClass(cls); } if (meta == null) { meta = noneValue; } for (Class c : list) { modifiedTypeMap.put(c.getName(), meta); } } catch (Exception e) { // e.printStackTrace(); } return meta == noneValue ? null : meta; } private List<ExtendedOpposite> opposites; public List<ExtendedOpposite> getLoadedOpposites() { if (! isUserDefinedTypeLoaded() || modifiedTypeMap == null) { return Collections.EMPTY_LIST; } if (opposites == null) { opposites = new ArrayList<ExtendedOpposite>(); Set<ExtendedOpposite> set = new HashSet<ExtendedOpposite>(); for (ModifiedTypeMeta meta : modifiedTypeMap.values()) { for (ExtendedReference ref : meta.getReferences()) { ExtendedOpposite o = ref.getOpposite(); if (o != null && set.add(o)) { opposites.add(o); } } } Collections.sort(opposites); } return opposites; } }