001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.common.component;
023    
024    import java.util.ArrayList;
025    import java.util.Collections;
026    import java.util.List;
027    import net.jcip.annotations.Immutable;
028    import org.jboss.dna.common.CommonI18n;
029    import org.jboss.dna.common.util.CheckArg;
030    import org.jboss.dna.common.util.ClassUtil;
031    
032    /**
033     * @author Randall Hauch
034     */
035    @Immutable
036    public class ComponentConfig implements Comparable<ComponentConfig> {
037    
038        private final String name;
039        private final String description;
040        private final String componentClassname;
041        private final List<String> classpath;
042        private final long timestamp;
043    
044        /**
045         * Create a component configuration.
046         * @param name the name of the configuration, which is considered to be a unique identifier
047         * @param description the description
048         * @param classname the name of the Java class used for the component
049         * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
050         * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
051         * Java classname
052         */
053        public ComponentConfig( String name, String description, String classname, String... classpath ) {
054            this(name, description, System.currentTimeMillis(), classname, classpath);
055        }
056    
057        /**
058         * Create a component configuration.
059         * @param name the name of the configuration, which is considered to be a unique identifier
060         * @param description the description
061         * @param timestamp the timestamp that this component was last changed
062         * @param classname the name of the Java class used for the component
063         * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
064         * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
065         * Java classname
066         */
067        public ComponentConfig( String name, String description, long timestamp, String classname, String... classpath ) {
068            CheckArg.isNotEmpty(name, "name");
069            this.name = name.trim();
070            this.description = description != null ? description.trim() : "";
071            this.componentClassname = classname;
072            this.classpath = buildList(classpath);
073            this.timestamp = timestamp;
074            // Check the classname is a valid classname ...
075            if (!ClassUtil.isFullyQualifiedClassname(classname)) {
076                throw new IllegalArgumentException(CommonI18n.componentClassnameNotValid.text(classname, name));
077            }
078        }
079    
080        /* package */static List<String> buildList( String... classpathElements ) {
081            List<String> classpath = null;
082            if (classpathElements != null) {
083                classpath = new ArrayList<String>();
084                for (String classpathElement : classpathElements) {
085                    if (!classpath.contains(classpathElement)) classpath.add(classpathElement);
086                }
087                classpath = Collections.unmodifiableList(classpath);
088            } else {
089                classpath = Collections.emptyList(); // already immutable
090            }
091            return classpath;
092        }
093    
094        /**
095         * Get the name of this component.
096         * @return the component name; never null, empty or blank
097         */
098        public String getName() {
099            return this.name;
100        }
101    
102        /**
103         * Get the description for this component
104         * @return the description
105         */
106        public String getDescription() {
107            return this.description;
108        }
109    
110        /**
111         * Get the fully-qualified name of the Java class used for instances of this component
112         * @return the Java class name of this component; never null or empty and always a valid Java class name
113         */
114        public String getComponentClassname() {
115            return this.componentClassname;
116        }
117    
118        /**
119         * Get the classpath defined in terms of strings compatible with a {@link ClassLoaderFactory}.
120         * @return the classpath; never null but possibly empty
121         */
122        public List<String> getComponentClasspath() {
123            return this.classpath;
124        }
125    
126        /**
127         * Get the classpath defined as an array of strings compatible with a {@link ClassLoaderFactory}.
128         * @return the classpath as an array; never null but possibly empty
129         */
130        public String[] getComponentClasspathArray() {
131            return this.classpath.toArray(new String[this.classpath.size()]);
132        }
133    
134        /**
135         * Get the system timestamp when this configuration object was created.
136         * @return the timestamp
137         */
138        public long getTimestamp() {
139            return this.timestamp;
140        }
141    
142        /**
143         * {@inheritDoc}
144         */
145        public int compareTo( ComponentConfig that ) {
146            if (that == this) return 0;
147            int diff = this.getName().compareToIgnoreCase(that.getName());
148            if (diff != 0) return diff;
149            diff = (int)(this.getTimestamp() - that.getTimestamp());
150            return diff;
151        }
152    
153        /**
154         * {@inheritDoc}
155         */
156        @Override
157        public int hashCode() {
158            return this.getName().hashCode();
159        }
160    
161        /**
162         * {@inheritDoc}
163         */
164        @Override
165        public boolean equals( Object obj ) {
166            if (obj == this) return true;
167            if (obj instanceof ComponentConfig) {
168                ComponentConfig that = (ComponentConfig)obj;
169                if (!this.getClass().equals(that.getClass())) return false;
170                return this.getName().equalsIgnoreCase(that.getName());
171            }
172            return false;
173        }
174    
175        /**
176         * Determine whether this component has changed with respect to the supplied component. This method basically checks all
177         * attributes, whereas {@link #equals(Object) equals} only checks the {@link #getClass() type} and {@link #getName()}.
178         * @param component the component to be compared with this one
179         * @return true if this componet and the supplied component have some changes, or false if they are exactly equivalent
180         * @throws IllegalArgumentException if the supplied component reference is null or is not the same {@link #getClass() type} as
181         * this object
182         */
183        public boolean hasChanged( ComponentConfig component ) {
184            CheckArg.isNotNull(component, "component");
185            CheckArg.isInstanceOf(component, this.getClass(), "component");
186            if (!this.getName().equalsIgnoreCase(component.getName())) return true;
187            if (!this.getDescription().equals(component.getDescription())) return true;
188            if (!this.getComponentClassname().equals(component.getComponentClassname())) return true;
189            if (!this.getComponentClasspath().equals(component.getComponentClasspath())) return true;
190            return false;
191        }
192    
193    }