/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.lookup.ui.attribute;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Iterator;
import java.util.Collections;
import com.artima.lookup.util.ConsistentMap;
/**
* UI attribute that enables clients to get a list of
* the fully qualified names and version numbers of packages
* required by a UI.
*
* <P>
* One or more <CODE>RequiredPackages</CODE> attributes may appear
* in the attributes of a <CODE>UIDescriptor</CODE>. Client programs
* interested in a UI may wish to verify that they have all required
* packages mentioned in the <CODE>RequiredPackages</CODE> attributes
* (if any) contained in the UI's <CODE>UIDescriptor</CODE>, before
* they attempt to create the UI. If the client is lacking any required
* packages (either because the entire package is absent or because the
* package is present but of an incompatible version), the client will
* not be able to use the UI,
*
* <P>
* The intent of this attribute is to provide a quick way for a client program
* to determine that a UI is unusable by a client, not to grant a guarantee that a UI
* is definitely usable by the client. If a client is missing a required package,
* or has an incompatible version of a required package, the client cannot use the UI.
* But if the client has compatible versions of all required packages listed in a
* <CODE>RequiredPackage</CODE> attribute, the client may or may not be able to
* use the UI.
*
* <P>
* UI providers should take bold and valiant strides to list in a <CODE>RequiredPackage</CODE>
* attribute all known packages required of the client, so that if
* the client discovers it has compatible versions of all listed packages and
* attempts to generate the UI via the factory method, the client will likely
* succeed. However, client programmers should bear in mind that a
* <CODE>RequiredPackage</CODE> attribute doesn't necessarily list
* <EM>all</EM> required packages. As a result, satisfying all required packages
* doesn't absolutely guarantee the UI will work on the client.
* As a result, client programs should program defensively.
* (For example, clients should probably catch <CODE>LinkageError</CODE>
* in appropriate places when dealing with UIs, even if they find they have
* compatible versions of all required packages listed in <CODE>RequiredPackage</CODE>
* attributes.)
*
* The version numbers must take the form of "specification version numbers," as used
* by the <CODE>java.lang.Package</CODE> class:
*
* <BLOCKQUOTE>
* Specification version numbers use a "Dewey Decimal" syntax that consists of positive
* decimal integers separated by periods ".", for example, "2.0" or "1.2.3.4.5.6.7". This
* allows an extensible number to be used to represent major, minor, micro, etc versions.
* The version number must begin with a number.
* </BLOCKQUOTE>
*
* @author Bill Venners
*/
public class RequiredPackages implements java.io.Serializable {
private static final long serialVersionUID = 1814117871506550968L;
/**
* @serial A <code>Map</code> of <code>String</code> keys to
* <code>String</code> values. The keys contained
* in the <CODE>Map</CODE> must be <CODE>String</CODE>s
* that represent fully qualified names of required packages.
* Each value contained in the <CODE>Map</CODE> must
* be the oldest version number of the package (defined by the
* key) that is compatible with the UI. Version numbers are
* <CODE>String</CODE>s in the form of "specification version
* numbers," as used by the <CODE>java.lang.Package</CODE> class.
*/
private Map requiredPackages;
/**
* Constructs a <CODE>RequiredPackages</CODE> attribute
* with the passed <CODE>Map</CODE>. The keys contained
* in the passed <CODE>Map</CODE> must be <CODE>String</CODE>s
* that represent fully qualified names of required packages.
* Each value contained in the passed <CODE>Map</CODE> must
* be the oldest version number of the package (defined by the
* key) that is compatible with the UI. Version numbers are
* <CODE>String</CODE>s in the form of
* "specification version numbers," as used
* by the <CODE>java.lang.Package</CODE> class. This constructor copies
* the contents of the passed <code>Map</code> into a
* serializable unmodifiable <code>Map</code> that has a
* consistent serialized form across all VMs.
*
* @param packages a map of <code>String</code> fully qualified
* names of required packages to <code>String</code> version
* numbers
*
* @throws NullPointerException if <CODE>packages</CODE>
* is <CODE>null</CODE> or if any keys or values contained
* in <CODE>packages</CODE> are <CODE>null</CODE>.
*
* @throws IllegalArgumentException if any non-null key or
* value contained in <CODE>packages</CODE> set is not an instance of
* <CODE>java.lang.String</CODE>.
*/
public RequiredPackages(Map packages) {
if (packages == null) {
throw new NullPointerException();
}
Set typeNames = packages.keySet();
Iterator it = typeNames.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o == null) {
throw new NullPointerException();
}
if (!(o instanceof String)) {
throw new IllegalArgumentException();
}
}
Collection versions = packages.values();
it = versions.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o == null) {
throw new NullPointerException();
}
if (!(o instanceof String)) {
throw new IllegalArgumentException();
}
}
requiredPackages = new ConsistentMap(packages);
}
/**
* Returns an iterator over the set of <CODE>String</CODE>
* fully qualified package names required
* by the UI generated by the UI factory stored in
* the marshalled object of the same <CODE>UIDescriptor</CODE>.
* The returned <CODE>Iterator</CODE> does not support
* <CODE>remove()</CODE>.
*
* @return an iterator over the set of <code>String</code>
* fully qualified names for required packages
*/
public Iterator iterator() {
return requiredPackages.keySet().iterator();
}
/**
* Returns a version number for the required package
* whose fully qualified package name is passed as
* the <CODE>packageName</CODE> parameter. If the
* passed <CODE>String</CODE> does not represent a
* required package listed in this <CODE>RequiredPackage</CODE>
* attribute, this method returns <CODE>null</CODE>.
*
* The version number returned should be a "specification version number," as used
* by the <CODE>java.lang.Package</CODE> class:
*
* <BLOCKQUOTE>
* Specification version numbers use a "Dewey Decimal" syntax that consists of positive
* decimal integers separated by periods ".", for example, "2.0" or "1.2.3.4.5.6.7". This
* allows an extensible number to be used to represent major, minor, micro, etc versions.
* The version number must begin with a number.
* </BLOCKQUOTE>
*
* @return the version number for the passed required package, or <code>null</code> if
* the passed package is not required
* @throws NullPointerException if <CODE>packageName</CODE>
* is <CODE>null</CODE>.
*/
public String getVersion(String packageName) {
if (packageName == null) {
throw new NullPointerException();
}
return (String) requiredPackages.get(packageName);
}
/**
* Returns a <CODE>java.util.Map</CODE> whose keys
* are <CODE>String</CODE>s that represent fully
* qualified names of required packages and whose
* values are be the oldest version number of the
* package (defined by the
* key) that is compatible with the UI. Version numbers are
* <CODE>String</CODE>s in the form of
* "specification version numbers," as used
* by the <CODE>java.lang.Package</CODE> class:
*
* The version numbers contained as values in the returned <CODE>Map</CODE>
* should be a "specification version number," as used
* by the <CODE>java.lang.Package</CODE> class:
*
* <BLOCKQUOTE>
* Specification version numbers use a "Dewey Decimal" syntax that consists of positive
* decimal integers separated by periods ".", for example, "2.0" or "1.2.3.4.5.6.7". This
* allows an extensible number to be used to represent major, minor, micro, etc versions.
* The version number must begin with a number.
* </BLOCKQUOTE>
*
* @return an unmodifiable map of package name keys to version number values
*/
public Map getRequiredPackages() {
return requiredPackages;
}
/**
* Compares the specified object (the <CODE>Object</CODE> passed
* in <CODE>o</CODE>) with this <CODE>RequiredPackages</CODE>
* object for equality. Returns true if the specified object
* is not null, if the specified object's class is
* <CODE>RequiredPackages</CODE>, if the two sets of
* package-version pairs are the same size, and if every package-version pair mentioned in the
* specified <CODE>RequiredPackages</CODE> object (passed in <CODE>o</CODE>) is also mentioned
* in this <CODE>RequiredPackages</CODE> object.
*
* @param o the object to compare against
* @return <code>true</code> if the objects are the same,
* <code>false</code> otherwise.
*/
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o.getClass() != RequiredPackages.class) {
return false;
}
RequiredPackages packages = (RequiredPackages) o;
if (!requiredPackages.equals(packages.requiredPackages)) {
return false;
}
return true;
}
/**
* Returns the hash code value for this <CODE>RequiredPackages</CODE> object.
*
* @return the hashcode for this object
*/
public int hashCode() {
return requiredPackages.hashCode();
}
}