All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.testifyproject.glassfish.jersey.model.internal.CommonConfig Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in org.testifyproject.testifyprojectpliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.org.testifyproject.testifyproject/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.testifyproject.glassfish.org.testifyproject.model.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.Priorities;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;

import javax.annotation.Priority;

import org.testifyproject.glassfish.org.testifyproject.ExtendedConfig;
import org.testifyproject.glassfish.org.testifyproject.internal.LocalizationMessages;
import org.testifyproject.glassfish.org.testifyproject.internal.ServiceFinder;
import org.testifyproject.glassfish.org.testifyproject.internal.inject.Binder;
import org.testifyproject.glassfish.org.testifyproject.internal.inject.CompositeBinder;
import org.testifyproject.glassfish.org.testifyproject.internal.inject.InjectionManager;
import org.testifyproject.glassfish.org.testifyproject.internal.spi.AutoDiscoverable;
import org.testifyproject.glassfish.org.testifyproject.internal.spi.ForcedAutoDiscoverable;
import org.testifyproject.glassfish.org.testifyproject.internal.util.PropertiesHelper;
import org.testifyproject.glassfish.org.testifyproject.model.ContractProvider;
import org.testifyproject.glassfish.org.testifyproject.process.Inflector;

/**
 * Common immutable {@link javax.ws.rs.core.Configuration} implementation for
 * server and client.
 *
 * @author Michal Gajdos
 * @author Marek Potociar (marek.potociar at oracle.org.testifyproject.testifyproject)
 */
public class CommonConfig implements FeatureContext, ExtendedConfig {

    private static final Logger LOGGER = Logger.getLogger(CommonConfig.class.getName());
    private static final Function CAST_TO_BINDER = Binder.class::cast;

    /**
     * Configuration runtime type.
     */
    private final RuntimeType type;
    /**
     * Configuration properties collection and it's immutable views.
     */
    private final Map properties;
    private final Map immutablePropertiesView;
    private final Collection immutablePropertyNames;
    /**
     * Configured providers, does not include features and binders.
     */
    private final ComponentBag org.testifyproject.testifyprojectponentBag;
    /**
     * Collection of unprocessed feature registrations.
     */
    private final List newFeatureRegistrations;
    /**
     * Collection of enabled feature classes.
     */
    private final Set> enabledFeatureClasses;
    /**
     * Collection of enabled feature instances.
     */
    private final Set enabledFeatures;

    /**
     * Flag determining whether the configuration of meta-providers (excl. binders) should be disabled.
     */
    private boolean disableMetaProviderConfiguration;

    /**
     * A single feature registration record.
     */
    private static final class FeatureRegistration {

        private final Class featureClass;
        private final Feature feature;

        private FeatureRegistration(final Class featureClass) {
            this.featureClass = featureClass;
            this.feature = null;
        }

        private FeatureRegistration(final Feature feature) {
            this.featureClass = feature.getClass();
            this.feature = feature;
        }

        /**
         * Get the registered feature class.
         *
         * @return registered feature class.
         */
        Class getFeatureClass() {
            return featureClass;
        }

        /**
         * Get the registered feature instance or {@code null} if this is a
         * class based feature registration.
         *
         * @return the registered feature instance or {@code null} if this is a
         *         class based feature registration.
         */
        public Feature getFeature() {
            return feature;
        }

        @Override
        public boolean equals(final Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof FeatureRegistration)) {
                return false;
            }
            final FeatureRegistration other = (FeatureRegistration) obj;

            return (featureClass == other.featureClass)
                    || (feature != null && (feature == other.feature || feature.equals(other.feature)));
        }

        @Override
        public int hashCode() {
            int hash = 47;
            hash = 13 * hash + (feature != null ? feature.hashCode() : 0);
            hash = 13 * hash + (featureClass != null ? featureClass.hashCode() : 0);
            return hash;
        }
    }

    /**
     * Create a new {@code RuntimeConfig} instance.
     * 

* The constructor provides a way for defining a {@link ContractProvider contract * provider model} registration strategy. Once a registration model is built * for a newly registered contract, the provided registration strategy filter is * consulted whether the model should be registered or not. *

*

* Clients can use the method to cancel any contract provider model registration * that does not meet the criteria of a given configuration context, such as a model * that does not have any recognized contracts associated with it. *

* * @param type configuration runtime type. * @param registrationStrategy function driving the decision (based on the introspected * {@link ContractProvider contract provider model}) whether * or not should the org.testifyproject.testifyprojectponent class registration continue * towards a successful org.testifyproject.testifyprojectpletion. */ public CommonConfig(final RuntimeType type, final Predicate registrationStrategy) { this.type = type; this.properties = new HashMap<>(); this.immutablePropertiesView = Collections.unmodifiableMap(properties); this.immutablePropertyNames = Collections.unmodifiableCollection(properties.keySet()); this.org.testifyproject.testifyprojectponentBag = ComponentBag.newInstance(registrationStrategy); this.newFeatureRegistrations = new LinkedList<>(); this.enabledFeatureClasses = Collections.newSetFromMap(new IdentityHashMap<>()); this.enabledFeatures = new HashSet<>(); this.disableMetaProviderConfiguration = false; } /** * Copy constructor. * * @param config configurable to copy class properties from. */ public CommonConfig(final CommonConfig config) { this.type = config.type; this.properties = new HashMap<>(config.properties.size()); this.immutablePropertiesView = Collections.unmodifiableMap(this.properties); this.immutablePropertyNames = Collections.unmodifiableCollection(this.properties.keySet()); this.org.testifyproject.testifyprojectponentBag = config.org.testifyproject.testifyprojectponentBag.copy(); this.newFeatureRegistrations = new LinkedList<>(); this.enabledFeatureClasses = Collections.newSetFromMap(new IdentityHashMap<>()); this.enabledFeatures = new HashSet<>(); copy(config, false); } /** * Copy config properties, providers from given {@code config} to this instance. * * @param config configurable to copy class properties from. * @param loadComponentBag {@code true} if the org.testifyproject.testifyprojectponent bag from config should be copied as well, {@code false} otherwise. */ private void copy(final CommonConfig config, final boolean loadComponentBag) { this.properties.clear(); this.properties.putAll(config.properties); this.newFeatureRegistrations.clear(); this.newFeatureRegistrations.addAll(config.newFeatureRegistrations); this.enabledFeatureClasses.clear(); this.enabledFeatureClasses.addAll(config.enabledFeatureClasses); this.enabledFeatures.clear(); this.enabledFeatures.addAll(config.enabledFeatures); this.disableMetaProviderConfiguration = config.disableMetaProviderConfiguration; if (loadComponentBag) { this.org.testifyproject.testifyprojectponentBag.loadFrom(config.org.testifyproject.testifyprojectponentBag); } } @Override public ExtendedConfig getConfiguration() { return this; } @Override public RuntimeType getRuntimeType() { return type; } @Override public Map getProperties() { return immutablePropertiesView; } @Override public Object getProperty(final String name) { return properties.get(name); } @Override public boolean isProperty(final String name) { return PropertiesHelper.isProperty(getProperty(name)); } @Override public Collection getPropertyNames() { return immutablePropertyNames; } @Override public boolean isEnabled(final Class featureClass) { return enabledFeatureClasses.contains(featureClass); } @Override public boolean isEnabled(final Feature feature) { return enabledFeatures.contains(feature); } @Override public boolean isRegistered(final Object org.testifyproject.testifyprojectponent) { return org.testifyproject.testifyprojectponentBag.getInstances().contains(org.testifyproject.testifyprojectponent); } @Override public boolean isRegistered(final Class org.testifyproject.testifyprojectponentClass) { return org.testifyproject.testifyprojectponentBag.getRegistrations().contains(org.testifyproject.testifyprojectponentClass); } @Override public Map, Integer> getContracts(final Class org.testifyproject.testifyprojectponentClass) { final ContractProvider model = org.testifyproject.testifyprojectponentBag.getModel(org.testifyproject.testifyprojectponentClass); return (model == null) ? Collections.emptyMap() : model.getContractMap(); } @Override public Set> getClasses() { return org.testifyproject.testifyprojectponentBag.getClasses(); } @Override public Set getInstances() { return org.testifyproject.testifyprojectponentBag.getInstances(); } /** * Returns a {@link ComponentBag} instance associated with the configuration. * * @return a non-null org.testifyproject.testifyprojectponent bag instance. */ public final ComponentBag getComponentBag() { return org.testifyproject.testifyprojectponentBag; } /** * An extension point that provides a way how to define a custom enhancement/update * operation of a contract provider model registration being produced for a given * org.testifyproject.testifyprojectponent class. * Default implementation return an enhancer just builds the model. *

* Derived implementations may use this method to e.g. filter out all contracts not * applicable in the given configuration context or change the model scope. The returned * set of filtered contracts is then used for the actual provider registration. *

* * @param org.testifyproject.testifyprojectponentClass class of the org.testifyproject.testifyprojectponent being registered. * @return filter for the contracts that being registered for a given org.testifyproject.testifyprojectponent class. */ protected Inflector getModelEnhancer(final Class org.testifyproject.testifyprojectponentClass) { return ComponentBag.AS_IS; } /** * Set the configured properties to the provided map of properties. * * @param properties new map of properties to be set. * @return updated configuration instance. */ public CommonConfig setProperties(final Map properties) { this.properties.clear(); if (properties != null) { this.properties.putAll(properties); } return this; } /** * Add properties to {@code ResourceConfig}. * * If any of the added properties exists already, he values of the existing * properties will be replaced with new values. * * @param properties properties to add. * @return updated configuration instance. */ public CommonConfig addProperties(final Map properties) { if (properties != null) { this.properties.putAll(properties); } return this; } @Override public CommonConfig property(final String name, final Object value) { if (value == null) { properties.remove(name); } else { properties.put(name, value); } return this; } @Override public CommonConfig register(final Class org.testifyproject.testifyprojectponentClass) { checkComponentClassNotNull(org.testifyproject.testifyprojectponentClass); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponentClass, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(null, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Class org.testifyproject.testifyprojectponentClass, final int bindingPriority) { checkComponentClassNotNull(org.testifyproject.testifyprojectponentClass); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponentClass, bindingPriority, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(null, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Class org.testifyproject.testifyprojectponentClass, final Class... contracts) { checkComponentClassNotNull(org.testifyproject.testifyprojectponentClass); if (contracts == null || contracts.length == 0) { LOGGER.warning(LocalizationMessages.COMPONENT_CONTRACTS_EMPTY_OR_NULL(org.testifyproject.testifyprojectponentClass)); return this; } if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponentClass, asNewIdentitySet(contracts), getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(null, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Class org.testifyproject.testifyprojectponentClass, final Map, Integer> contracts) { checkComponentClassNotNull(org.testifyproject.testifyprojectponentClass); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponentClass, contracts, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(null, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Object org.testifyproject.testifyprojectponent) { checkProviderNotNull(org.testifyproject.testifyprojectponent); final Class org.testifyproject.testifyprojectponentClass = org.testifyproject.testifyprojectponent.getClass(); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponent, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(org.testifyproject.testifyprojectponent, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Object org.testifyproject.testifyprojectponent, final int bindingPriority) { checkProviderNotNull(org.testifyproject.testifyprojectponent); final Class org.testifyproject.testifyprojectponentClass = org.testifyproject.testifyprojectponent.getClass(); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponent, bindingPriority, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(org.testifyproject.testifyprojectponent, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Object org.testifyproject.testifyprojectponent, final Class... contracts) { checkProviderNotNull(org.testifyproject.testifyprojectponent); final Class org.testifyproject.testifyprojectponentClass = org.testifyproject.testifyprojectponent.getClass(); if (contracts == null || contracts.length == 0) { LOGGER.warning(LocalizationMessages.COMPONENT_CONTRACTS_EMPTY_OR_NULL(org.testifyproject.testifyprojectponentClass)); return this; } if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponent, asNewIdentitySet(contracts), getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(org.testifyproject.testifyprojectponent, org.testifyproject.testifyprojectponentClass); } return this; } @Override public CommonConfig register(final Object org.testifyproject.testifyprojectponent, final Map, Integer> contracts) { checkProviderNotNull(org.testifyproject.testifyprojectponent); final Class org.testifyproject.testifyprojectponentClass = org.testifyproject.testifyprojectponent.getClass(); if (org.testifyproject.testifyprojectponentBag.register(org.testifyproject.testifyprojectponent, contracts, getModelEnhancer(org.testifyproject.testifyprojectponentClass))) { processFeatureRegistration(org.testifyproject.testifyprojectponent, org.testifyproject.testifyprojectponentClass); } return this; } private void processFeatureRegistration(final Object org.testifyproject.testifyprojectponent, final Class org.testifyproject.testifyprojectponentClass) { final ContractProvider model = org.testifyproject.testifyprojectponentBag.getModel(org.testifyproject.testifyprojectponentClass); if (model.getContracts().contains(Feature.class)) { @SuppressWarnings("unchecked") final FeatureRegistration registration = (org.testifyproject.testifyprojectponent != null) ? new FeatureRegistration((Feature) org.testifyproject.testifyprojectponent) : new FeatureRegistration((Class) org.testifyproject.testifyprojectponentClass); newFeatureRegistrations.add(registration); } } /** * Load the internal configuration state from an externally provided configuration state. *

* Calling this method effectively replaces existing configuration state of the instance with the state represented by the * externally provided configuration. If the features, auto-discoverables of given config has been already configured then * this method will make sure to not configure them for the second time. * * @param config external configuration state to replace the configuration of this configurable instance. * @return the updated org.testifyproject.testifyprojectmon configuration instance. */ public CommonConfig loadFrom(final Configuration config) { if (config instanceof CommonConfig) { // If loading from CommonConfig then simply copy properties and check whether given config has been initialized. final CommonConfig org.testifyproject.testifyprojectmonConfig = (CommonConfig) config; copy(org.testifyproject.testifyprojectmonConfig, true); this.disableMetaProviderConfiguration = !org.testifyproject.testifyprojectmonConfig.enabledFeatureClasses.isEmpty(); } else { setProperties(config.getProperties()); this.enabledFeatures.clear(); this.enabledFeatureClasses.clear(); org.testifyproject.testifyprojectponentBag.clear(); resetRegistrations(); for (final Class clazz : config.getClasses()) { if (Feature.class.isAssignableFrom(clazz) && config.isEnabled((Class) clazz)) { this.disableMetaProviderConfiguration = true; } register(clazz, config.getContracts(clazz)); } for (final Object instance : config.getInstances()) { if (instance instanceof Feature && config.isEnabled((Feature) instance)) { this.disableMetaProviderConfiguration = true; } register(instance, config.getContracts(instance.getClass())); } } return this; } private Set> asNewIdentitySet(final Class... contracts) { final Set> result = Collections.newSetFromMap(new IdentityHashMap<>()); result.addAll(Arrays.asList(contracts)); return result; } private void checkProviderNotNull(final Object provider) { if (provider == null) { throw new IllegalArgumentException(LocalizationMessages.COMPONENT_CANNOT_BE_NULL()); } } private void checkComponentClassNotNull(final Class org.testifyproject.testifyprojectponentClass) { if (org.testifyproject.testifyprojectponentClass == null) { throw new IllegalArgumentException(LocalizationMessages.COMPONENT_CLASS_CANNOT_BE_NULL()); } } /** * Configure {@link AutoDiscoverable auto-discoverables} in the injection manager. * * @param injectionManager injection manager in which the auto-discoverables should be configured. * @param autoDiscoverables list of registered auto discoverable org.testifyproject.testifyprojectponents. * @param forcedOnly defines whether all or only forced auto-discoverables should be configured. */ public void configureAutoDiscoverableProviders(final InjectionManager injectionManager, final Collection autoDiscoverables, final boolean forcedOnly) { // Check whether meta providers have been initialized for a config this config has been loaded from. if (!disableMetaProviderConfiguration) { final Set providers = new TreeSet<>((o1, o2) -> { final int p1 = o1.getClass().isAnnotationPresent(Priority.class) ? o1.getClass().getAnnotation(Priority.class).value() : Priorities.USER; final int p2 = o2.getClass().isAnnotationPresent(Priority.class) ? o2.getClass().getAnnotation(Priority.class).value() : Priorities.USER; return (p1 < p2 || p1 == p2) ? -1 : 1; }); // Forced (always invoked). final List forcedAutoDiscroverables = new LinkedList<>(); for (Class forcedADType : ServiceFinder.find(ForcedAutoDiscoverable.class, true) .toClassArray()) { forcedAutoDiscroverables.add(injectionManager.createAndInitialize(forcedADType)); } providers.addAll(forcedAutoDiscroverables); // Regular. if (!forcedOnly) { providers.addAll(autoDiscoverables); } for (final AutoDiscoverable autoDiscoverable : providers) { final ConstrainedTo constrainedTo = autoDiscoverable.getClass().getAnnotation(ConstrainedTo.class); if (constrainedTo == null || type.equals(constrainedTo.value())) { try { autoDiscoverable.configure(this); } catch (final Exception e) { LOGGER.log(Level.FINE, LocalizationMessages.AUTODISCOVERABLE_CONFIGURATION_FAILED(autoDiscoverable.getClass()), e); } } } } } /** * Configure binders in the injection manager and enable JAX-RS features. * * @param injectionManager injection manager in which the binders and features should be configured. */ public void configureMetaProviders(InjectionManager injectionManager, ManagedObjectsFinalizer finalizer) { // First, configure existing binders Set configuredBinders = configureBinders(injectionManager, Collections.emptySet()); // Check whether meta providers have been initialized for a config this config has been loaded from. if (!disableMetaProviderConfiguration) { // Register external meta objects configureExternalObjects(injectionManager); // Next, configure all features configureFeatures(injectionManager, new HashSet<>(), resetRegistrations(), finalizer); // At last, configure any new binders added by features configureBinders(injectionManager, configuredBinders); } } private Set configureBinders(InjectionManager injectionManager, Set configured) { Set allConfigured = Collections.newSetFromMap(new IdentityHashMap<>()); allConfigured.addAll(configured); Collection binders = getBinder(configured); if (!binders.isEmpty()) { injectionManager.register(CompositeBinder.wrap(binders)); allConfigured.addAll(binders); } return allConfigured; } private Collection getBinder(Set configured) { return org.testifyproject.testifyprojectponentBag.getInstances(ComponentBag.BINDERS_ONLY) .stream() .map(CAST_TO_BINDER) .filter(binder -> !configured.contains(binder)) .collect(Collectors.toList()); } private void configureExternalObjects(InjectionManager injectionManager) { org.testifyproject.testifyprojectponentBag.getInstances(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager)) .forEach(injectionManager::register); org.testifyproject.testifyprojectponentBag.getClasses(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager)) .forEach(injectionManager::register); } private void configureFeatures(InjectionManager injectionManager, Set processed, List unprocessed, ManagedObjectsFinalizer managedObjectsFinalizer) { FeatureContextWrapper featureContextWrapper = null; for (final FeatureRegistration registration : unprocessed) { if (processed.contains(registration)) { LOGGER.config(LocalizationMessages.FEATURE_HAS_ALREADY_BEEN_PROCESSED(registration.getFeatureClass())); continue; } Feature feature = registration.getFeature(); if (feature == null) { feature = injectionManager.createAndInitialize(registration.getFeatureClass()); managedObjectsFinalizer.registerForPreDestroyCall(feature); } else { // Disable injection of Feature instances on the client-side. Instances may be registered into multiple // web-targets which means that injecting anything into these instances is not safe. if (!RuntimeType.CLIENT.equals(type)) { injectionManager.inject(feature); } } if (enabledFeatures.contains(feature)) { LOGGER.config(LocalizationMessages.FEATURE_HAS_ALREADY_BEEN_PROCESSED(feature)); continue; } if (featureContextWrapper == null) { // init lazily featureContextWrapper = new FeatureContextWrapper(this, injectionManager); } final boolean success = feature.configure(featureContextWrapper); if (success) { processed.add(registration); configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer); enabledFeatureClasses.add(registration.getFeatureClass()); enabledFeatures.add(feature); } } } private List resetRegistrations() { final List result = new ArrayList<>(newFeatureRegistrations); newFeatureRegistrations.clear(); return result; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof CommonConfig)) { return false; } final CommonConfig that = (CommonConfig) o; if (type != that.type) { return false; } if (!properties.equals(that.properties)) { return false; } if (!org.testifyproject.testifyprojectponentBag.equals(that.org.testifyproject.testifyprojectponentBag)) { return false; } if (!enabledFeatureClasses.equals(that.enabledFeatureClasses)) { return false; } if (!enabledFeatures.equals(that.enabledFeatures)) { return false; } if (!newFeatureRegistrations.equals(that.newFeatureRegistrations)) { return false; } return true; } @Override public int hashCode() { int result = type.hashCode(); result = 31 * result + properties.hashCode(); result = 31 * result + org.testifyproject.testifyprojectponentBag.hashCode(); result = 31 * result + newFeatureRegistrations.hashCode(); result = 31 * result + enabledFeatures.hashCode(); result = 31 * result + enabledFeatureClasses.hashCode(); return result; } }