/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.ui.internal;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.ui.PlatformUI;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xmind.core.Core;
import org.xmind.core.IAdaptable;
import org.xmind.core.IDeserializer;
import org.xmind.core.IManifest;
import org.xmind.core.ISerializer;
import org.xmind.core.IWorkbook;
import org.xmind.core.event.ICoreEventListener;
import org.xmind.core.event.ICoreEventRegistration;
import org.xmind.core.event.ICoreEventSource;
import org.xmind.core.event.ICoreEventSupport;
import org.xmind.core.internal.MarkerGroup;
import org.xmind.core.internal.MarkerVariation;
import org.xmind.core.internal.dom.StyleSheetImpl;
import org.xmind.core.internal.event.CoreEventSupport;
import org.xmind.core.internal.zip.ArchiveConstants;
import org.xmind.core.io.BundleResource;
import org.xmind.core.io.DirectoryInputSource;
import org.xmind.core.io.DirectoryOutputTarget;
import org.xmind.core.io.DirectoryStorage;
import org.xmind.core.marker.AbstractMarkerResource;
import org.xmind.core.marker.IMarker;
import org.xmind.core.marker.IMarkerGroup;
import org.xmind.core.marker.IMarkerResource;
import org.xmind.core.marker.IMarkerResourceAllocator;
import org.xmind.core.marker.IMarkerResourceProvider;
import org.xmind.core.marker.IMarkerSheet;
import org.xmind.core.marker.IMarkerVariation;
import org.xmind.core.style.IStyle;
import org.xmind.core.style.IStyleSheet;
import org.xmind.core.util.DOMUtils;
import org.xmind.core.util.FileUtils;
import org.xmind.core.util.IPropertiesProvider;
import org.xmind.ui.mindmap.IResourceManager;
import org.xmind.ui.mindmap.IResourceManagerListener;
import org.xmind.ui.mindmap.ITemplate;
import org.xmind.ui.mindmap.ITemplateGroup;
import org.xmind.ui.mindmap.IWorkbookRef;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.prefs.PrefConstants;
import org.xmind.ui.util.Logger;
import org.xmind.ui.util.ResourceFinder;
public class MindMapResourceManager implements IResourceManager {
private static final String DEFAULT_THEME_ID = "xminddefaultthemeid"; //$NON-NLS-1$
private static final String PATH_MARKERS = "markers/"; //$NON-NLS-1$
private static final String PATH_USER_MARKERS = "markers/"; //$NON-NLS-1$
private static final String PATH_STYLES = "styles/"; //$NON-NLS-1$
private static final String PATH_STYLES_DIR = "$nl$/styles/"; //$NON-NLS-1$
private static final String PATH_USER_STYLES = PATH_STYLES + "userStyles/"; //$NON-NLS-1$
private static final String PATH_USER_THEMES = PATH_STYLES + "userThemes/"; //$NON-NLS-1$
private static final String MARKER_SHEET_XML = "markerSheet.xml"; //$NON-NLS-1$
private static final String MARKER_SHEET = "markerSheet"; //$NON-NLS-1$
private static final String DEFAULT_STYLES_XML = "defaultStyles.xml"; //$NON-NLS-1$
private static final String STYLES = "styles"; //$NON-NLS-1$
private static final String THEMES = "themes"; //$NON-NLS-1$
private static final String EXT_PROPERTIES = ".properties"; //$NON-NLS-1$
private static final String EXT_XML = ".xml"; //$NON-NLS-1$
private static final String SYS_TEMPLATES_DIR = "$nl$/templates/"; //$NON-NLS-1$
private static final String SYS_TEMPLATES_XML_PATH = SYS_TEMPLATES_DIR
+ "templates.xml"; //$NON-NLS-1$
private static final String USER_TEMPLATES_DIR = "templates/"; //$NON-NLS-1$
private static class SystemMarkerResourceProvider
implements IMarkerResourceProvider {
public IMarkerResource getMarkerResource(IMarker marker) {
return new SystemMarkerResource(marker);
}
public boolean isPermanent() {
return true;
}
}
private static class SystemMarkerResource extends AbstractMarkerResource {
public SystemMarkerResource(IMarker marker) {
super(marker, PATH_MARKERS);
}
public InputStream getInputStream() {
return getInputStreamForPath(getFullPath(), 100);
}
@Override
public InputStream openInputStream(int zoom) throws IOException {
return getInputStreamForPath(getFullPath(), zoom);
}
@Override
public InputStream openInputStream(IMarkerVariation variation, int zoom)
throws IOException {
return getInputStreamForPath(variation.getVariedPath(getFullPath()),
zoom);
}
private InputStream getInputStreamForPath(String fullPath, int zoom) {
String xfullPath = getxPath(fullPath, zoom);
URL url = find(xfullPath);
if (url == null)
url = find(fullPath);
if (url == null)
return null;
try {
return url.openStream();
} catch (IOException e) {
}
return null;
}
private String getxPath(String path, int zoom) {
int dot = path.lastIndexOf('.');
if (dot != -1 && zoom > 100) {
String lead = path.substring(0, dot);
String tail = path.substring(dot);
String x = "@2x"; //$NON-NLS-1$
return lead + x + tail;
}
return path;
}
public OutputStream getOutputStream() {
return null;
}
public InputStream openInputStream() throws IOException {
URL url = find(getFullPath());
if (url == null)
throw new FileNotFoundException(getFullPath());
return url.openStream();
}
public OutputStream openOutputStream() throws IOException {
throw new FileNotFoundException("System marker is read only."); //$NON-NLS-1$
}
@Override
protected void loadVariations(List<IMarkerVariation> variations) {
IMarkerVariation v = new MarkerVariation("@16", 16, 16); //$NON-NLS-1$
if (find(v.getVariedPath(getFullPath())) != null)
variations.add(v);
v = new MarkerVariation("@24", 24, 24); //$NON-NLS-1$
if (find(v.getVariedPath(getFullPath())) != null)
variations.add(v);
v = new MarkerVariation("@32", 32, 32); //$NON-NLS-1$
if (find(v.getVariedPath(getFullPath())) != null)
variations.add(v);
}
@Override
public InputStream getInputStream(IMarkerVariation variation) {
return getInputStreamForPath(variation.getVariedPath(getFullPath()),
100);
}
@Override
public InputStream openInputStream(IMarkerVariation variation)
throws IOException {
InputStream stream = getInputStreamForPath(
variation.getVariedPath(getFullPath()), 100);
if (stream == null)
throw new FileNotFoundException();
return stream;
}
@Override
public OutputStream getOutputStream(IMarkerVariation variation) {
return null;
}
@Override
public OutputStream openOutputStream(IMarkerVariation variation)
throws IOException {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof SystemMarkerResource))
return false;
return super.equals(obj);
}
}
private static class UserMarkerResourceProvider
implements IMarkerResourceProvider, IMarkerResourceAllocator {
public IMarkerResource getMarkerResource(IMarker marker) {
return new UserMarkerResource(marker);
}
public boolean isPermanent() {
return false;
}
/*
* (non-Javadoc)
* @see org.xmind.core.marker.IMarkerResourceAllocator#
* allocateMarkerResourcePath(java.io.InputStream, java.lang.String)
*/
@Override
public String allocateMarkerResource(InputStream source,
String suggestedPath) throws IOException {
String ext = suggestedPath == null ? ".png" //$NON-NLS-1$
: FileUtils.getExtension(suggestedPath);
String path = Core.getIdFactory().createId() + ext;
File file = new File(Core.getWorkspace()
.getAbsolutePath(PATH_USER_MARKERS + path));
FileUtils.ensureFileParent(file);
OutputStream target = new FileOutputStream(file);
try {
FileUtils.transfer(source, target, false);
} finally {
target.close();
}
return path;
}
}
private static class UserMarkerResource extends AbstractMarkerResource {
private final String JPG_FORMAT = "jpg"; //$NON-NLS-1$
private final String JPEG_FORMAT = "jpeg"; //$NON-NLS-1$
private final String PNG_FORMAT = "png"; //$NON-NLS-1$
public UserMarkerResource(IMarker marker) {
super(marker, PATH_USER_MARKERS);
}
private File getFile() {
File origin = FileUtils.ensureFileParent(new File(
Core.getWorkspace().getAbsolutePath(getFullPath())));
String lowerFullPath = getFullPath().toLowerCase();
if (lowerFullPath.endsWith(JPEG_FORMAT)
|| lowerFullPath.endsWith(JPG_FORMAT)) {
try {
String jpg = Core.getWorkspace()
.getAbsolutePath(getFullPath());
BufferedImage source = ImageIO.read(new File(jpg));
String png = jpg.substring(0, jpg.lastIndexOf('.') - 1)
+ PNG_FORMAT;
File pngFile = new File(png);
ImageIO.write(source, PNG_FORMAT, pngFile);
return pngFile;
} catch (Exception e) {
}
}
return origin;
}
public InputStream getInputStream() {
File file = getFile();
if (file != null)
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
}
return null;
}
public OutputStream getOutputStream() {
File file = getFile();
if (file != null)
try {
return new FileOutputStream(file);
} catch (FileNotFoundException e) {
}
return null;
}
public InputStream openInputStream() throws IOException {
return new FileInputStream(getFile());
}
public OutputStream openOutputStream() throws IOException {
return new FileOutputStream(getFile());
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof UserMarkerResource))
return false;
return super.equals(obj);
}
}
protected static class RecentMarkerGroup extends MarkerGroup
implements ICoreEventSource {
public static final RecentMarkerGroup instance = new RecentMarkerGroup();
private static final int CAPACITY = 20;
private List<IMarker> markers = new ArrayList<IMarker>(CAPACITY);
private ICoreEventSupport eventSupport = new CoreEventSupport();
private RecentMarkerGroup() {
}
public void addMarker(IMarker marker) {
if (markers.contains(marker))
return;
while (markers.size() >= CAPACITY) {
markers.remove(markers.size() - 1);
}
markers.add(0, marker);
eventSupport.dispatchTargetChange(this, Core.MarkerAdd, marker);
}
public <T> T getAdapter(Class<T> adapter) {
if (adapter == ICoreEventSource.class)
return adapter.cast(this);
return super.getAdapter(adapter);
}
public List<IMarker> getMarkers() {
return markers;
}
/*
* (non-Javadoc)
* @see org.xmind.core.marker.IMarkerGroup#isEmpty()
*/
@Override
public boolean isEmpty() {
return markers.isEmpty();
}
public String getName() {
return MindMapMessages.RecentUsed;
}
public void setSingleton(boolean singleton) {
}
public IMarkerSheet getOwnedSheet() {
return null;
}
public IMarkerSheet getParent() {
return null;
}
public boolean isSingleton() {
return false;
}
public boolean isHidden() {
return false;
}
public void setHidden(boolean hidden) {
}
public void removeMarker(IMarker marker) {
if (!markers.contains(marker))
return;
markers.remove(marker);
eventSupport.dispatchTargetChange(this, Core.MarkerRemove, marker);
}
public void setName(String name) {
}
public String getId() {
return "org.xmind.ui.RecentMarkerGroup"; //$NON-NLS-1$
}
public Object getRegisterKey() {
return getId();
}
public ICoreEventRegistration registerCoreEventListener(String type,
ICoreEventListener listener) {
return eventSupport.registerCoreEventListener(this, type, listener);
}
public int hashCode() {
return super.hashCode();
}
public ICoreEventSupport getCoreEventSupport() {
return eventSupport;
}
}
private IMarkerSheet systemMarkerSheet = null;
private IMarkerSheet userMarkerSheet = null;
private IMarkerGroup recentMarkerGroup = null;
private IStyleSheet defaultStyleSheet = null;
private IStyleSheet systemStyleSheet = null;
private IWorkbook userStylesContainer = null;
private IStyle blankTheme = null;
private IStyle defaultTheme = null;
private IStyleSheet systemThemeSheet = null;
private IWorkbook userThemesContainer = null;
private ITemplate defaultTemplate;
private ListenerList resourceManagerListeners = new ListenerList();
/*
* (non-Javadoc)
* @see org.xmind.ui.internal.IMarkerSheetManager#getSystemMarkerSheet()
*/
public IMarkerSheet getSystemMarkerSheet() {
if (systemMarkerSheet == null) {
systemMarkerSheet = createSystemMarkerShet();
}
return systemMarkerSheet;
}
private IMarkerSheet createSystemMarkerShet() {
URL url = find(PATH_MARKERS, MARKER_SHEET_XML);
if (url != null) {
try {
IMarkerSheet sheet = Core.getMarkerSheetBuilder()
.loadFromURL(url, new SystemMarkerResourceProvider());
loadPropertiesFor(sheet, PATH_MARKERS, MARKER_SHEET);
return sheet;
} catch (Exception e) {
Logger.log(e, "Failed to load system marker from: " + url); //$NON-NLS-1$
}
}
return Core.getMarkerSheetBuilder()
.createMarkerSheet(new SystemMarkerResourceProvider());
}
/*
* (non-Javadoc)
* @see org.xmind.ui.internal.IMarkerSheetManager#getUserMarkerSheet()
*/
public IMarkerSheet getUserMarkerSheet() {
if (userMarkerSheet == null) {
userMarkerSheet = createUserMarkerSheet();
initUserMarkerSheet(userMarkerSheet);
}
return userMarkerSheet;
}
public void saveUserMarkerSheet() {
if (userMarkerSheet != null) {
String path = Core.getWorkspace()
.getAbsolutePath(PATH_USER_MARKERS + MARKER_SHEET_XML);
File file = FileUtils.ensureFileParent(new File(path));
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
userMarkerSheet.save(out);
} catch (Exception e) {
Logger.log(e);
} finally {
if (out != null)
try {
out.close();
} catch (IOException e) {
Logger.log(e);
}
}
}
}
private void initUserMarkerSheet(IMarkerSheet sheet) {
sheet.setParentSheet(getSystemMarkerSheet());
}
private IMarkerSheet createUserMarkerSheet() {
String path = Core.getWorkspace()
.getAbsolutePath(PATH_USER_MARKERS + MARKER_SHEET_XML);
if (path != null) {
File file = new File(path);
if (file.exists()) {
try {
return Core.getMarkerSheetBuilder().loadFromFile(file,
new UserMarkerResourceProvider());
} catch (Exception e) {
Logger.log(e, "Failed to load user marker from: " + file); //$NON-NLS-1$
}
}
}
return Core.getMarkerSheetBuilder()
.createMarkerSheet(new UserMarkerResourceProvider());
}
public IMarkerGroup getRecentMarkerGroup() {
if (recentMarkerGroup == null) {
recentMarkerGroup = new RecentMarkerGroup();
}
return recentMarkerGroup;
}
public IStyleSheet getDefaultStyleSheet() {
if (defaultStyleSheet == null) {
defaultStyleSheet = createDefaultStyleSheet();
}
return defaultStyleSheet;
}
private IStyleSheet createDefaultStyleSheet() {
URL url = find(PATH_STYLES, DEFAULT_STYLES_XML);
if (url != null) {
try {
return Core.getStyleSheetBuilder().loadFromUrl(url);
} catch (Exception e) {
Logger.log(e, "Failed to load default styles: " + url); //$NON-NLS-1$
}
}
return Core.getStyleSheetBuilder().createStyleSheet();
}
public IStyleSheet getSystemStyleSheet() {
if (systemStyleSheet == null) {
systemStyleSheet = createSystemStyleSheet();
IManifest manifest = Core.getWorkbookBuilder().createWorkbook()
.getManifest();
((StyleSheetImpl) systemStyleSheet).setManifest(manifest);
}
return systemStyleSheet;
}
private IStyleSheet createSystemStyleSheet() {
URL url = find(PATH_STYLES, STYLES, EXT_XML);
if (url != null) {
try {
IStyleSheet sheet = Core.getStyleSheetBuilder()
.loadFromUrl(url);
loadPropertiesFor(sheet, PATH_STYLES, STYLES);
return sheet;
} catch (Exception e) {
Logger.log(e, "Falied to load saved styles: " + url); //$NON-NLS-1$
}
}
return Core.getStyleSheetBuilder().createStyleSheet();
}
public IStyleSheet getUserStyleSheet() {
if (userStylesContainer == null) {
userStylesContainer = loadResourceContainer(PATH_USER_STYLES);
}
return userStylesContainer.getStyleSheet();
}
public void saveUserStyleSheet() {
saveResourceContainer(userStylesContainer, PATH_USER_STYLES);
}
public IStyle getBlankTheme() {
if (blankTheme == null) {
blankTheme = Core.getStyleSheetBuilder().createStyleSheet()
.createStyle(IStyle.THEME);
blankTheme.setName(MindMapMessages.DefaultTheme_title);
}
return blankTheme;
}
public IStyle getDefaultTheme() {
if (defaultTheme == null) {
defaultTheme = findDefaultTheme();
}
return checkDefaultTheme(defaultTheme);
}
private IStyle checkDefaultTheme(IStyle defaultTheme) {
String defaultId = defaultTheme.getId();
boolean exist = getSystemThemeSheet().findStyle(defaultId) != null
|| getUserThemeSheet().findStyle(defaultId) != null
|| getBlankTheme().getId().equals(defaultId);
return exist ? defaultTheme
: getSystemThemeSheet().findStyle(DEFAULT_THEME_ID);
}
private IStyle findDefaultTheme() {
if (Platform.isRunning()) {
String defaultId = MindMapUIPlugin.getDefault().getPreferenceStore()
.getString(PrefConstants.DEFUALT_THEME);
if (defaultId != null && !"".equals(defaultId)) { //$NON-NLS-1$
IStyle theme = getSystemThemeSheet().findStyle(defaultId);
if (theme == null) {
// theme = getUserStyleSheet().findStyle(defaultId);
theme = getUserThemeSheet().findStyle(defaultId);
}
if (theme == null && defaultId.equals(getBlankTheme().getId()))
theme = getBlankTheme();
if (theme != null)
return theme;
}
}
return getSystemThemeSheet().findStyle(DEFAULT_THEME_ID);
}
public void setDefaultTheme(String id) {
IStyle theme = null;
if (id != null && !"".equals(id)) { //$NON-NLS-1$
theme = getSystemThemeSheet().findStyle(DEFAULT_THEME_ID);
if (!id.equals(theme.getId())) {
theme = getSystemThemeSheet().findStyle(id);
if (theme == null) {
theme = getUserThemeSheet().findStyle(id);
}
if (theme == null && id.equals(getBlankTheme().getId())) {
theme = getBlankTheme();
}
}
}
if (theme == null)
id = null;
this.defaultTheme = theme;
MindMapUIPlugin.getDefault().getPreferenceStore()
.setValue(PrefConstants.DEFUALT_THEME, id);
}
public IStyleSheet getSystemThemeSheet() {
if (systemThemeSheet == null) {
systemThemeSheet = createSystemThemeSheet();
IManifest manifest = Core.getWorkbookBuilder().createWorkbook()
.getManifest();
((StyleSheetImpl) systemThemeSheet).setManifest(manifest);
}
return systemThemeSheet;
}
private IStyleSheet createSystemThemeSheet() {
// URL url = find(PATH_STYLES, THEMES_XML);
URL url = find(PATH_STYLES_DIR, THEMES, EXT_XML);
if (url != null) {
try {
IStyleSheet sheet = Core.getStyleSheetBuilder()
.loadFromUrl(url);
loadPropertiesFor(sheet, PATH_STYLES, THEMES);
return sheet;
} catch (Exception e) {
Logger.log(e, "Falied to load system themes: " + url); //$NON-NLS-1$
}
}
return Core.getStyleSheetBuilder().createStyleSheet();
}
public IStyleSheet getUserThemeSheet() {
if (userThemesContainer == null) {
userThemesContainer = loadResourceContainer(PATH_USER_THEMES);
}
return userThemesContainer.getStyleSheet();
}
public void saveUserThemeSheet() {
saveResourceContainer(userThemesContainer, PATH_USER_THEMES);
}
/**
* @param sourcePath
* @return
*/
private static IWorkbook loadResourceContainer(String sourcePath) {
IWorkbook container = null;
File file = new File(Core.getWorkspace().getAbsolutePath(sourcePath));
File tempLoc = new File(Core.getWorkspace().getTempDir(sourcePath));
FileUtils.ensureDirectory(tempLoc);
DirectoryStorage tempStorage = new DirectoryStorage(tempLoc);
if (file.exists() && file.isDirectory()
&& new File(file, ArchiveConstants.CONTENT_XML).exists()) {
try {
IDeserializer deserializer = Core.getWorkbookBuilder()
.newDeserializer();
deserializer.setWorkbookStorage(tempStorage);
deserializer.setInputSource(new DirectoryInputSource(file));
deserializer.deserialize(null);
container = deserializer.getWorkbook();
} catch (Exception e) {
Logger.log(e, "Failed to load user styles from: " + file); //$NON-NLS-1$
}
}
if (container == null) {
container = Core.getWorkbookBuilder().createWorkbook(tempStorage);
}
return container;
}
private static void saveResourceContainer(IWorkbook workbook,
String targetPath) {
if (workbook == null)
return;
File file = new File(Core.getWorkspace().getAbsolutePath(targetPath));
FileUtils.ensureDirectory(file);
try {
ISerializer serializer = Core.getWorkbookBuilder().newSerializer();
serializer.setWorkbook(workbook);
serializer.setOutputTarget(new DirectoryOutputTarget(file));
serializer.serialize(null);
} catch (Exception e) {
Logger.log(e);
}
}
private static URL find(String fullPath) {
Bundle bundle = Platform.getBundle(MindMapUI.PLUGIN_ID);
if (bundle != null) {
return FileLocator.find(bundle, new Path(fullPath), null);
}
return null;
}
private static URL find(String mainPath, String subPath) {
return find(mainPath + subPath);
}
private static URL find(String mainPath, String prefix, String suffix) {
return ResourceFinder.findResource(MindMapUI.PLUGIN_ID, mainPath,
prefix, suffix);
}
private static void loadPropertiesFor(IAdaptable resourceManager,
String mainPath, String propertiesFilePrefix) {
IPropertiesProvider propertiesProvider = (IPropertiesProvider) resourceManager
.getAdapter(IPropertiesProvider.class);
if (propertiesProvider == null)
return;
Properties defaultProperties = new Properties();
URL defaultPropertiesURL = FileLocator.find(
Platform.getBundle(MindMapUI.PLUGIN_ID),
new Path(mainPath + propertiesFilePrefix + EXT_PROPERTIES),
null);
if (defaultPropertiesURL != null) {
try {
InputStream stream = defaultPropertiesURL.openStream();
try {
defaultProperties.load(stream);
} finally {
stream.close();
}
} catch (IOException e) {
Logger.log(e, "Failed to load default properties file from: " //$NON-NLS-1$
+ mainPath + propertiesFilePrefix + EXT_PROPERTIES);
}
}
Properties properties = new Properties(defaultProperties);
URL propertiesURL = find(mainPath, propertiesFilePrefix,
EXT_PROPERTIES);
if (propertiesURL != null) {
try {
InputStream stream = propertiesURL.openStream();
try {
properties.load(stream);
} finally {
stream.close();
}
} catch (IOException e) {
Logger.log(e,
"Failed to load locale-specific properties file from: " //$NON-NLS-1$
+ mainPath + propertiesFilePrefix
+ EXT_PROPERTIES);
}
}
propertiesProvider.setProperties(properties);
}
private static final String SCHEMA_MARKER = "marker"; //$NON-NLS-1$
private static final String SCHEMA_STYLE = "style"; //$NON-NLS-1$
private static final String SCHEMA_THEME = "theme"; //$NON-NLS-1$
private static final String CATEGORY_SYSTEM = "system"; //$NON-NLS-1$
private static final String CATEGORY_USER = "user"; //$NON-NLS-1$
private static final String CATEGORY_DEFAULT = "default"; //$NON-NLS-1$
private static final String GROUP_ANY = "any"; //$NON-NLS-1$
private static final String ID_BLANK = "blank"; //$NON-NLS-1$
private static final String ID_DEFAULT = "__default__"; //$NON-NLS-1$
/*
* (non-Javadoc)
* @see org.xmind.ui.mindmap.IResourceManager#findResource(java.lang.String)
*/
public Object findResource(String uri) {
if (uri == null)
return null;
int sep = uri.indexOf(':');
if (sep < 0)
return null;
String schema = uri.substring(0, sep);
String path = uri.substring(sep + 1);
String[] segments = path.split("/"); //$NON-NLS-1$
if (segments.length == 0)
// Not possible to happen:
return null;
String category = segments[0];
if (SCHEMA_MARKER.equals(schema)) {
// marker:
IMarkerSheet markerSheet = findMarkerSheet(category);
if (markerSheet == null || segments.length == 1)
return markerSheet;
String groupId = segments[1];
boolean anyGroup = GROUP_ANY.equals(groupId);
IMarkerGroup markerGroup = anyGroup ? null
: markerSheet.findMarkerGroup(groupId);
if (segments.length == 2)
return markerGroup;
String markerId = segments[2];
if (markerGroup == null)
return anyGroup ? markerSheet.findMarker(markerId) : null;
return markerGroup.getMarker(markerId);
} else if (SCHEMA_STYLE.equals(schema)) {
// style:
IStyleSheet styleSheet = findStyleSheet(category);
if (styleSheet == null || segments.length == 1)
return styleSheet;
String styleId = segments[1];
return styleSheet.findStyle(styleId);
} else if (SCHEMA_THEME.equals(schema)) {
// theme:
IStyleSheet themeSheet = findThemeSheet(category);
if (themeSheet == null || segments.length == 1)
return themeSheet;
String styleId = segments[1];
if (themeSheet == getSystemThemeSheet()) {
if (ID_BLANK.equals(styleId))
return getBlankTheme();
if (ID_DEFAULT.equals(styleId))
return getDefaultTheme();
}
return themeSheet.findStyle(styleId);
}
return null;
}
private IMarkerSheet findMarkerSheet(String category) {
if (CATEGORY_SYSTEM.equals(category)) {
return getSystemMarkerSheet();
} else if (CATEGORY_USER.equals(category)) {
return getUserMarkerSheet();
} else {
return null;
}
}
private IStyleSheet findStyleSheet(String category) {
if (CATEGORY_DEFAULT.equals(category))
return getDefaultStyleSheet();
if (CATEGORY_SYSTEM.equals(category))
return getSystemStyleSheet();
if (CATEGORY_USER.equals(category))
return getUserStyleSheet();
return null;
}
private IStyleSheet findThemeSheet(String category) {
if (CATEGORY_SYSTEM.equals(category))
return getSystemThemeSheet();
if (CATEGORY_USER.equals(category))
return getUserThemeSheet();
return null;
}
/*
* (non-Javadoc)
* @see
* org.xmind.ui.mindmap.IResourceManager#toResourceURI(java.lang.Object)
*/
public String toResourceURI(Object resource) {
if (resource instanceof IMarkerSheet) {
if (resource == getSystemMarkerSheet())
return "marker:system"; //$NON-NLS-1$
if (resource == getUserMarkerSheet())
return "marker:user"; //$NON-NLS-1$
return null;
}
if (resource instanceof IMarkerGroup) {
IMarkerGroup markerGroup = (IMarkerGroup) resource;
IMarkerSheet markerSheet = markerGroup.getParent();
if (markerSheet == getSystemMarkerSheet())
return "marker:system/" + markerGroup.getId(); //$NON-NLS-1$
if (markerSheet == getUserMarkerSheet())
return "marker:user/" + markerGroup.getId(); //$NON-NLS-1$
return null;
}
if (resource instanceof IMarker) {
IMarker marker = (IMarker) resource;
IMarkerGroup markerGroup = marker.getParent();
if (markerGroup == null)
return null;
IMarkerSheet markerSheet = markerGroup.getParent();
if (markerSheet == getSystemMarkerSheet())
return "marker:system/" + markerGroup.getId() + "/" //$NON-NLS-1$ //$NON-NLS-2$
+ marker.getId();
if (markerSheet == getUserMarkerSheet())
return "marker:user/" + markerGroup.getId() + "/" //$NON-NLS-1$ //$NON-NLS-2$
+ marker.getId();
return null;
}
if (resource instanceof IStyleSheet) {
if (resource == getDefaultStyleSheet())
return "style:default"; //$NON-NLS-1$
if (resource == getSystemStyleSheet())
return "style:system"; //$NON-NLS-1$
if (resource == getUserStyleSheet())
return "style:user"; //$NON-NLS-1$
if (resource == getSystemThemeSheet())
return "theme:system"; //$NON-NLS-1$
if (resource == getUserThemeSheet())
return "theme:user"; //$NON-NLS-1$
}
if (resource instanceof IStyle) {
if (resource == getBlankTheme())
return "theme:system/blank"; //$NON-NLS-1$
IStyle style = (IStyle) resource;
IStyleSheet styleSheet = style.getOwnedStyleSheet();
if (styleSheet == getDefaultStyleSheet())
return "style:default/" + style.getId(); //$NON-NLS-1$
if (styleSheet == getSystemStyleSheet())
return "style:system/" + style.getId(); //$NON-NLS-1$
if (styleSheet == getUserStyleSheet())
return "style:user/" + style.getId(); //$NON-NLS-1$
if (styleSheet == getSystemThemeSheet())
return "theme:system/" + style.getId(); //$NON-NLS-1$
if (styleSheet == getUserThemeSheet())
return "theme:user/" + style.getId(); //$NON-NLS-1$
}
return null;
}
public List<ITemplate> getSystemTemplates() {
List<ITemplate> sysTemplates = new ArrayList<ITemplate>();
loadSystemTemplates(sysTemplates);
return sysTemplates;
}
public List<ITemplateGroup> getSystemTemplateGroups() {
List<ITemplateGroup> sysTemplateGroups = new ArrayList<ITemplateGroup>();
loadSystemTemplateGroups(sysTemplateGroups);
return sysTemplateGroups;
}
private void loadSystemTemplateGroups(
List<ITemplateGroup> sysTemplateGroups) {
Bundle bundle = Platform.getBundle(MindMapUI.PLUGIN_ID);
if (bundle == null)
return;
BundleResource listXMLResource = new BundleResource(bundle,
new Path(SYS_TEMPLATES_XML_PATH)).resolve();
if (listXMLResource == null) {
MindMapUIPlugin.getDefault().getLog()
.log(new Status(IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID,
"Failed to locate system template xml: " //$NON-NLS-1$
+ bundle.getSymbolicName() + "/" //$NON-NLS-1$
+ SYS_TEMPLATES_XML_PATH));
return;
}
URL listXMLURL = listXMLResource.toPlatformURL();
Element element = getTemplateListElement(listXMLURL);
if (element == null)
return;
Properties properties = getTemplateListProperties(bundle);
Iterator<Element> categoryIt = DOMUtils.childElementIterByTag(element,
"category"); //$NON-NLS-1$
while (categoryIt.hasNext()) {
Element categoryEle = categoryIt.next();
String name = categoryEle.getAttribute("name"); //$NON-NLS-1$
if (name.startsWith("%")) { //$NON-NLS-1$
if (properties != null) {
name = properties.getProperty(name.substring(1));
} else {
name = null;
}
}
TemplateGroup templateGroup = new TemplateGroup(name);
Iterator<Element> templateIt = DOMUtils
.childElementIterByTag(categoryEle, "template"); //$NON-NLS-1$
ArrayList<ITemplate> templates = new ArrayList<ITemplate>();
loadTemplates(templates, templateIt);
templateGroup.setTemplates(templates);
sysTemplateGroups.add(templateGroup);
}
}
private void loadSystemTemplates(List<ITemplate> templates) {
Bundle bundle = Platform.getBundle(MindMapUI.PLUGIN_ID);
if (bundle == null)
return;
BundleResource listXMLResource = new BundleResource(bundle,
new Path(SYS_TEMPLATES_XML_PATH)).resolve();
if (listXMLResource == null) {
MindMapUIPlugin.getDefault().getLog()
.log(new Status(IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID,
"Failed to locate system template xml: " //$NON-NLS-1$
+ bundle.getSymbolicName() + "/" //$NON-NLS-1$
+ SYS_TEMPLATES_XML_PATH));
return;
}
URL listXMLURL = listXMLResource.toPlatformURL();
Element element = getTemplateListElement(listXMLURL);
if (element == null)
return;
Iterator<Element> it = DOMUtils.childElementIterByTag(element,
"template"); //$NON-NLS-1$
loadTemplates(templates, it);
}
private void loadTemplates(List<ITemplate> templates,
Iterator<Element> it) {
while (it.hasNext()) {
Element templateEle = it.next();
String resource = templateEle.getAttribute("resource"); //$NON-NLS-1$
if (resource == null || "".equals(resource)) //$NON-NLS-1$
continue;
URI resourceURI;
try {
resourceURI = URIUtil.toURI(new URL(resource));
} catch (IOException e) {
MindMapUIPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID,
"Failed to load system template: " + resource, e)); //$NON-NLS-1$
continue;
} catch (URISyntaxException e) {
MindMapUIPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID,
"Failed to load system template: " + resource, e)); //$NON-NLS-1$
continue;
}
if (!resourceURI.isAbsolute()) {
BundleResource templateResource = new BundleResource(
Platform.getBundle(MindMapUI.PLUGIN_ID),
new Path(SYS_TEMPLATES_DIR + resource)).resolve();
try {
resourceURI = templateResource.toPlatformURL().toURI();
} catch (URISyntaxException e) {
MindMapUIPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID,
"Failed to load system template: " + resource, e)); //$NON-NLS-1$
continue;
}
}
String name = templateEle.getAttribute("name"); //$NON-NLS-1$
if (name == null || "".equals(name)) { //$NON-NLS-1$
name = FileUtils.getNoExtensionFileName(resource);
}
templates.add(new ClonedTemplate(resourceURI, name));
}
}
private Properties getTemplateListProperties(Bundle bundle) {
URL propURL = ResourceFinder.findResource(bundle, SYS_TEMPLATES_DIR,
"templates", EXT_PROPERTIES); //$NON-NLS-1$
if (propURL == null) {
MindMapUIPlugin.getDefault().getLog().log(new Status(
IStatus.WARNING, MindMapUIPlugin.PLUGIN_ID,
"Failed to find template NLS properties file: " //$NON-NLS-1$
+ bundle.getSymbolicName() + "/" + SYS_TEMPLATES_DIR //$NON-NLS-1$
+ "templates_XX.properties")); //$NON-NLS-1$
return null;
}
try {
InputStream is = propURL.openStream();
try {
Properties properties = new Properties();
properties.load(is);
return properties;
} finally {
is.close();
}
} catch (IOException e) {
MindMapUIPlugin.getDefault().getLog()
.log(new Status(IStatus.WARNING, MindMapUIPlugin.PLUGIN_ID,
"Failed to load template NLS properties from " //$NON-NLS-1$
+ propURL.toExternalForm(),
e));
}
return null;
}
private Element getTemplateListElement(URL xmlURL) {
xmlURL = FileLocator.find(xmlURL);
try {
InputStream is = xmlURL.openStream();
if (is != null) {
try {
Document doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(is);
if (doc != null)
return doc.getDocumentElement();
} finally {
is.close();
}
}
} catch (Throwable e) {
MindMapUIPlugin.getDefault().getLog()
.log(new Status(IStatus.WARNING, MindMapUIPlugin.PLUGIN_ID,
"Failed to load template list from " //$NON-NLS-1$
+ xmlURL.toExternalForm(),
e));
}
return null;
}
private File createNonConflictingFile(File rootDir, String fileName) {
int dotIndex = fileName.lastIndexOf('.');
String name = dotIndex < 0 ? fileName : fileName.substring(0, dotIndex);
String ext = dotIndex < 0 ? "" : fileName.substring(dotIndex); //$NON-NLS-1$
File targetFile = new File(rootDir, fileName);
int i = 1;
while (targetFile.exists()) {
i++;
targetFile = new File(rootDir,
String.format("%s %s%s", name, i, ext)); //$NON-NLS-1$
}
return targetFile;
}
@Override
public List<ITemplate> getUserTemplates() {
List<ITemplate> customTemplates = new ArrayList<ITemplate>();
loadUserTemplates(customTemplates);
return customTemplates;
}
private void loadUserTemplates(List<ITemplate> templates) {
loadTemplatesFromDir(templates, getUserTemplatesDir());
}
private static File getUserTemplatesDir() {
return new File(
Core.getWorkspace().getAbsolutePath(USER_TEMPLATES_DIR));
}
private void loadTemplatesFromDir(List<ITemplate> templates,
File templatesDir) {
List<ITemplate> list = new ArrayList<ITemplate>();
if (templatesDir != null && templatesDir.isDirectory()) {
for (String fileName : templatesDir.list()) {
if (fileName.endsWith(MindMapUI.FILE_EXT_TEMPLATE)
|| fileName.endsWith(MindMapUI.FILE_EXT_XMIND)) {
File file = new File(templatesDir, fileName);
if ((file.isFile() && file.canRead())
|| file.isDirectory()) {
list.add(new ClonedTemplate(file.toURI(), null));
}
}
}
}
Collections.sort(list, new Comparator<ITemplate>() {
public int compare(ITemplate t1, ITemplate t2) {
if (!(t1 instanceof ClonedTemplate)
|| !(t2 instanceof ClonedTemplate))
return 0;
ClonedTemplate ct1 = (ClonedTemplate) t1;
ClonedTemplate ct2 = (ClonedTemplate) t2;
File f1 = URIUtil.toFile(ct1.getSourceWorkbookURI());
File f2 = URIUtil.toFile(ct2.getSourceWorkbookURI());
if (f1 == null || f2 == null || !f1.exists() || !f2.exists())
return 0;
return (int) (f1.lastModified() - f2.lastModified());
}
});
templates.addAll(list);
}
@Override
public ITemplate addUserTemplateFromWorkbookURI(URI workbookURI)
throws InvocationTargetException {
Assert.isNotNull(workbookURI);
final IWorkbookRef sourceWorkbookRef = MindMapUIPlugin.getDefault()
.getWorkbookRefFactory().createWorkbookRef(workbookURI, null);
if (sourceWorkbookRef == null)
throw new IllegalArgumentException(
"Invalid workbook URI: " + workbookURI); //$NON-NLS-1$
final File userTemplateFile = createUserTemplateOutputFile(
sourceWorkbookRef.getName() + MindMapUI.FILE_EXT_TEMPLATE);
final URI templateURI = userTemplateFile.toURI();
final IWorkbookRef templateWorkbookRef = MindMapUIPlugin.getDefault()
.getWorkbookRefFactory().createWorkbookRef(templateURI, null);
if (templateWorkbookRef == null)
throw new IllegalStateException(
"Failed to obtain workbook ref for local file URI: " //$NON-NLS-1$
+ templateURI);
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
try {
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
sourceWorkbookRef.open(subMonitor.newChild(30));
try {
templateWorkbookRef.importFrom(subMonitor.newChild(60),
sourceWorkbookRef);
} finally {
subMonitor.setWorkRemaining(10);
sourceWorkbookRef.close(subMonitor.newChild(10));
}
} finally {
if (monitor != null)
monitor.done();
}
}
};
try {
if (PlatformUI.isWorkbenchRunning()) {
PlatformUI.getWorkbench().getProgressService().run(false, true,
runnable);
} else {
runnable.run(new NullProgressMonitor());
}
} catch (InterruptedException e) {
// canceled
return null;
}
ITemplate template = new ClonedTemplate(templateURI, FileUtils
.getNoExtensionFileName(userTemplateFile.getAbsolutePath()));
fireUserTemplateAdded(template);
return template;
}
private void fireUserTemplateAdded(final ITemplate template) {
for (final Object listener : resourceManagerListeners.getListeners()) {
SafeRunner.run(new SafeRunnable() {
@Override
public void run() throws Exception {
((IResourceManagerListener) listener)
.userTemplateAdded(template);
}
});
}
}
private void fireUserTemplateRemoved(final ITemplate template) {
for (final Object listener : resourceManagerListeners.getListeners()) {
SafeRunner.run(new SafeRunnable() {
@Override
public void run() throws Exception {
((IResourceManagerListener) listener)
.userTemplateRemoved(template);
}
});
}
}
private File createUserTemplateOutputFile(String fileName) {
File dir = getUserTemplatesDir();
FileUtils.ensureDirectory(dir);
File file = createNonConflictingFile(dir, fileName);
file.mkdirs();
return file;
}
@Override
public void removeUserTemplate(ITemplate template) {
URI templateURI = template.getSourceWorkbookURI();
if (URIUtil.isFileURI(templateURI)) {
File templateFile = URIUtil.toFile(templateURI);
File templatesDir = getUserTemplatesDir();
if (templatesDir.equals(templateFile.getParentFile())) {
FileUtils.delete(templateFile);
fireUserTemplateRemoved(template);
}
}
}
@Override
public void addResourceManagerListener(IResourceManagerListener listener) {
resourceManagerListeners.add(listener);
}
@Override
public void removeResourceManagerListener(
IResourceManagerListener listener) {
resourceManagerListeners.remove(listener);
}
@Override
public ITemplate getDefaultTemplate() {
return this.defaultTemplate;
}
@Override
public void setDefaultTemplate(ITemplate defaultTemplate) {
this.defaultTemplate = defaultTemplate;
}
@Override
public boolean isUserTemplate(ITemplate template) {
URI templateURI = template.getSourceWorkbookURI();
if (URIUtil.isFileURI(templateURI)) {
File templateFile = URIUtil.toFile(templateURI);
File templatesDir = getUserTemplatesDir();
return templatesDir.equals(templateFile.getParentFile());
}
return false;
}
@Override
public boolean isSystemTemplate(ITemplate template) {
// TODO check source workbook URI to determine system template
boolean isSysTemplate = getSystemTemplates().contains(template);
List<ITemplateGroup> systemTemplateGroups = getSystemTemplateGroups();
for (ITemplateGroup group : systemTemplateGroups) {
if (group.getTemplates().contains(template))
return true;
}
return isSysTemplate;
}
}