/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2012 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.extension; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.servoy.extension.parser.EXPParser; import com.servoy.extension.parser.IEXPParserPool; /** * This class provides extension info & data taken from all extension packages within an OS directory or from a single file. * * @author acostescu */ @SuppressWarnings("nls") public class FileBasedExtensionProvider extends CachingExtensionProvider implements IFileBasedExtensionProvider { public static final String EXTENSION_PACKAGE_FILE_EXTENSION = ".exp"; protected final File file; protected boolean thinkDir; protected IEXPParserPool parserSource; protected boolean extensionXMLsParsed = false; protected Map<String, Map<String, File>> extensionVersionToFile; // <extensionid, <version, File>> protected MessageKeeper messages = new MessageKeeper(); /** * Creates a new file based extension provider. It can use a directory of .exp files or a single .exp file. * @param file the file or folder. * @param thinkDir if it should be considered a directory; false for file. */ public FileBasedExtensionProvider(File file, boolean thinkDir, IEXPParserPool parserSource) { if (!file.exists() || !file.canRead() || (thinkDir && !file.isDirectory()) || (!thinkDir && !file.isFile())) { throw new IllegalArgumentException("'" + file + "' is not a valid/accessible directory/file."); } this.file = file; this.thinkDir = thinkDir; this.parserSource = parserSource; } @Override protected DependencyMetadata[] getDependencyMetadataImpl(ExtensionDependencyDeclaration extensionDependency) { // so this is a cache miss in super DependencyMetadata[] result; addCachedDependencyMetadataVersionInterval(extensionDependency.id, new VersionInterval(VersionStringUtils.UNBOUNDED, VersionStringUtils.UNBOUNDED)); if (extensionXMLsParsed) { result = null; // all available extension packages were already parsed/cached, so if there was a cache miss, the dependency is just not there } else { parseExtensionXMLs(); // parses and caches all available dependencies result = getDependencyMetadata(extensionDependency); // from cache } return result; } public DependencyMetadata[] getAllAvailableExtensions() { if (!extensionXMLsParsed) { parseExtensionXMLs(); // fill up cache } List<DependencyMetadata> allAvailable = new ArrayList<DependencyMetadata>(); Iterator<List<DependencyMetadata>> it = cachedDependencyMetadata.values().iterator(); while (it.hasNext()) { allAvailable.addAll(it.next()); } return allAvailable.toArray(new DependencyMetadata[allAvailable.size()]); } /** * Parses all available extension packages and caches the dependency meta-data. */ protected void parseExtensionXMLs() { extensionXMLsParsed = true; File[] fileList = thinkDir ? file.listFiles() : new File[] { file }; int count = 0; if (fileList != null) { for (File f : fileList) { if (f.exists() && f.isFile() && f.getName().endsWith(EXTENSION_PACKAGE_FILE_EXTENSION)) { count++; parseExtensionXML(f); } } } if (count == 0 && !thinkDir) { messages.addWarning("The file is not an extension package: '" + file.getAbsolutePath() + "'."); } } /** * Parses one extension package and caches dependency meta-data. * @param f the .exp file. */ protected void parseExtensionXML(File f) { EXPParser parser = ((parserSource == null) ? new EXPParser(f) : parserSource.getOrCreateParser(f)); DependencyMetadata dependencyMetadata = parser.parseDependencyInfo(); // cache dependency info about this version of the extension if (dependencyMetadata != null) { boolean added = cacheDependencyMetadataVersion(dependencyMetadata); if (added) { // tell cache that any version of this extension is already cached (because all available packages will be cached) addCachedDependencyMetadataVersionInterval(dependencyMetadata.id, new VersionInterval(VersionStringUtils.UNBOUNDED, VersionStringUtils.UNBOUNDED)); associateExtensionVersionWithFile(dependencyMetadata.id, dependencyMetadata.version, f); } else { messages.addWarning("More then one package contains extension ('" + dependencyMetadata.id + "', " + dependencyMetadata.version + "). Ignoring package: " + f.getName() + "."); } } messages.addAll(parser.getMessages()); parser.clearMessages(); } protected void associateExtensionVersionWithFile(String extensionId, String version, File zipFile) { if (extensionVersionToFile == null) extensionVersionToFile = new HashMap<String, Map<String, File>>(); Map<String, File> versions = extensionVersionToFile.get(extensionId); if (versions == null) { versions = new HashMap<String, File>(); extensionVersionToFile.put(extensionId, versions); } versions.put(version, zipFile); } public File getEXPFile(String extensionId, String version, IProgress progressMonitor) { if (!extensionXMLsParsed) parseExtensionXMLs(); File f = null; if (extensionVersionToFile != null) { Map<String, File> tmp = extensionVersionToFile.get(extensionId); if (tmp != null) { f = tmp.get(version); } } return f; } /** * If problems were encountered while reading contents of given directory/file, they will be remembered and returned by this method. * @return any problems encountered that might be of interest to the user. */ public Message[] getMessages() { return messages.getMessages(); } public void clearMessages() { messages.clearMessages(); } @Override public void flushCache() { extensionXMLsParsed = false; extensionVersionToFile = null; clearMessages(); super.flushCache(); } public void dispose() { // not much to do here as we don't keep resources allocated flushCache(); } }