123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- /*
- * Copyright (c) 2006-2011 DMDirc Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.plugins;
-
- import com.dmdirc.updater.Version;
- import com.dmdirc.util.collections.MapList;
-
- import java.io.File;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Map;
-
- /**
- * Validates that the values specified in a plugin's meta data are correct and
- * all requirements are met.
- *
- * @since 0.6.6
- */
- public class PluginMetaDataValidator {
-
- /** The metadata being validated. */
- private final PluginMetaData metadata;
-
- /** A collection of errors which have been identified. */
- private final Collection<String> errors
- = new ArrayList<String>();
-
- /**
- * Creates a new metadata validator.
- *
- * @param metadata The metadata to be validated
- */
- public PluginMetaDataValidator(final PluginMetaData metadata) {
- this.metadata = metadata;
- }
-
- /**
- * Validates the metadata file.
- *
- * @param plugins A map of known plugins to their short names
- * @param services A map of known services
- * @return A collection of errors that occurred (if any)
- */
- public Collection<String> validate(final Map<String, PluginMetaData> plugins,
- final MapList<String, String> services) {
- errors.clear();
-
- checkMetaData();
- checkRequirements(plugins, services);
-
- return Collections.unmodifiableCollection(errors);
- }
-
- /**
- * Checks that the metadata values are correct.
- */
- protected void checkMetaData() {
- if (!metadata.getVersion().isValid()) {
- errors.add("Missing or invalid 'version'");
- }
-
- if (metadata.getAuthor() == null || metadata.getAuthor().isEmpty()) {
- errors.add("Missing or invalid 'author'");
- }
-
- if (metadata.getName() == null || metadata.getName().isEmpty() || metadata.getName().indexOf(' ') != -1) {
- errors.add("Missing or invalid 'name'");
- }
-
- if (metadata.getMainClass() == null || metadata.getMainClass().isEmpty()) {
- errors.add("Missing or invalid 'mainclass'");
- }
- }
-
- /**
- * Checks that the plugin's requirements are met.
- *
- * @param plugins A map of known plugins by their short names
- * @param services A map of known services
- */
- protected void checkRequirements(final Map<String, PluginMetaData> plugins,
- final MapList<String, String> services) {
- checkOS(metadata.getRequirements().get("os"), System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch"));
- checkFiles(metadata.getRequirements().get("files"));
- checkPlugins(plugins, metadata.getRequirements().get("plugins"));
- checkServices(services, metadata.getRequiredServices());
- }
-
- /**
- * Check if the services required by this plugin are available.
- *
- * @param knownServices A map of known services
- * @param services Required services
- */
- private void checkServices(final MapList<String, String> knownServices,
- final Collection<String> services) {
- if (services == null || services.isEmpty()) {
- return;
- }
-
- for (String requirement : services) {
- final String[] bits = requirement.split(" ", 2);
- final String name = bits[0];
- final String type = bits[1];
-
- if (!knownServices.containsKey(type)
- || (!name.equalsIgnoreCase("any")
- && !knownServices.containsValue(type, name))) {
- errors.add("Service " + name + " of type " + type
- + " not available");
- }
- }
- }
-
- /**
- * Checks to see if the OS requirements of the plugin are satisfied.
- * If the desired string is empty, the test passes.
- * Otherwise it is used as one to three colon-delimited regular expressions,
- * to test the name, version and architecture of the OS, respectively.
- * On failure, the requirementsError field will contain a user-friendly
- * error message.
- *
- * @param desired The desired OS requirements
- * @param actualName The actual name of the OS
- * @param actualVersion The actual version of the OS
- * @param actualArch The actual architecture of the OS
- */
- protected void checkOS(final String desired, final String actualName, final String actualVersion, final String actualArch) {
- if (desired == null || desired.isEmpty()) {
- return;
- }
-
- final String[] desiredParts = desired.split(":");
-
- if (!actualName.toLowerCase().matches(desiredParts[0])) {
- errors.add("Invalid OS. (Wanted: '" + desiredParts[0] + "', actual: '" + actualName + "')");
- } else if (desiredParts.length > 1 && !actualVersion.toLowerCase().matches(desiredParts[1])) {
- errors.add("Invalid OS version. (Wanted: '" + desiredParts[1] + "', actual: '" + actualVersion + "')");
- } else if (desiredParts.length > 2 && !actualArch.toLowerCase().matches(desiredParts[2])) {
- errors.add("Invalid OS architecture. (Wanted: '" + desiredParts[2] + "', actual: '" + actualArch + "')");
- }
- }
-
- /**
- * Checks to see if the file requirements of the plugin are satisfied.
- * If the desired string is empty, the test passes.
- * Otherwise it is passed to File.exists() to see if the file is valid.
- * Multiple files can be specified by using a "," to separate. And either/or
- * files can be specified using a "|" (eg /usr/bin/bash|/bin/bash)
- * If the test fails, the requirementsError field will contain a
- * user-friendly error message.
- *
- * @param desired The desired file requirements
- */
- protected void checkFiles(final String desired) {
- if (desired == null || desired.isEmpty()) {
- return;
- }
-
- for (String files : desired.split(",")) {
- final String[] filelist = files.split("\\|");
- boolean foundFile = false;
- for (String file : filelist) {
- if (new File(file).exists()) {
- foundFile = true;
- break;
- }
- }
- if (!foundFile) {
- errors.add("Required file '" + files + "' not found");
- }
- }
- }
-
- /**
- * Checks to see if the plugin requirements of the plugin are satisfied.
- * If the desired string is empty, the test passes.
- * Plugins should be specified as:
- * plugin1[:minversion[:maxversion]],plugin2[:minversion[:maxversion]]
- * Plugins will be attempted to be loaded if not loaded, else the test will
- * fail if the versions don't match, or the plugin isn't known.
- * If the test fails, the requirementsError field will contain a
- * user-friendly error message.
- *
- * @param plugins A map of known plugins by their short names
- * @param desired The desired file requirements
- */
- protected void checkPlugins(final Map<String, PluginMetaData> plugins,
- final String desired) {
- if (desired == null || desired.isEmpty()) {
- return;
- }
-
- for (String pluginName : desired.split(",")) {
- final String[] data = pluginName.split(":");
- final PluginMetaData target = plugins.get(data[0]);
-
- if (target == null) {
- errors.add("Required plugin '" + data[0] + "' was not found");
- }
-
- if (data.length > 1) {
- // Check plugin minimum version matches.
- if (target.getVersion().compareTo(new Version(data[1])) < 0) {
- errors.add("Plugin '" + data[0]
- + "' is too old (required version: " + data[1]
- + ", actual version: " + target.getVersion() + ")");
- }
-
- // Check plugin maximum version matches.
- if (data.length > 2 && target.getVersion().compareTo(
- new Version(data[2])) > 0) {
- errors.add("Plugin '" + data[0]
- + "' is too new (required version: " + data[2]
- + ", actual version: " + target.getVersion() + ")");
- }
- }
- }
- }
-
- }
|