1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolVersion.java $
3 * $Revision: 609106 $
4 * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License.  You may obtain a copy of the License at
14 *
15 *   http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http;
33
34import java.io.Serializable;
35import org.apache.http.util.CharArrayBuffer;
36
37
38/**
39 * Represents a protocol version, as specified in RFC 2616.
40 * RFC 2616 specifies only HTTP versions, like "HTTP/1.1" and "HTTP/1.0".
41 * RFC 3261 specifies a message format that is identical to HTTP except
42 * for the protocol name. It defines a protocol version "SIP/2.0".
43 * There are some nitty-gritty differences between the interpretation
44 * of versions in HTTP and SIP. In those cases, HTTP takes precedence.
45 * <p>
46 * This class defines a protocol version as a combination of
47 * protocol name, major version number, and minor version number.
48 * Note that {@link #equals} and {@link #hashCode} are defined as
49 * final here, they cannot be overridden in derived classes.
50 *
51 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
52 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
53 *
54 * @version $Revision: 609106 $
55 */
56public class ProtocolVersion implements Serializable, Cloneable {
57
58    private static final long serialVersionUID = 8950662842175091068L;
59
60
61    /** Name of the protocol. */
62    protected final String protocol;
63
64    /** Major version number of the protocol */
65    protected final int major;
66
67    /** Minor version number of the protocol */
68    protected final int minor;
69
70
71    /**
72     * Create a protocol version designator.
73     *
74     * @param protocol   the name of the protocol, for example "HTTP"
75     * @param major      the major version number of the protocol
76     * @param minor      the minor version number of the protocol
77     */
78    public ProtocolVersion(String protocol, int major, int minor) {
79        if (protocol == null) {
80            throw new IllegalArgumentException
81                ("Protocol name must not be null.");
82        }
83        if (major < 0) {
84            throw new IllegalArgumentException
85                ("Protocol major version number must not be negative.");
86        }
87        if (minor < 0) {
88            throw new IllegalArgumentException
89                ("Protocol minor version number may not be negative");
90        }
91        this.protocol = protocol;
92        this.major = major;
93        this.minor = minor;
94    }
95
96    /**
97     * Returns the name of the protocol.
98     *
99     * @return the protocol name
100     */
101    public final String getProtocol() {
102        return protocol;
103    }
104
105    /**
106     * Returns the major version number of the protocol.
107     *
108     * @return the major version number.
109     */
110    public final int getMajor() {
111        return major;
112    }
113
114    /**
115     * Returns the minor version number of the HTTP protocol.
116     *
117     * @return the minor version number.
118     */
119    public final int getMinor() {
120        return minor;
121    }
122
123
124    /**
125     * Obtains a specific version of this protocol.
126     * This can be used by derived classes to instantiate themselves instead
127     * of the base class, and to define constants for commonly used versions.
128     * <br/>
129     * The default implementation in this class returns <code>this</code>
130     * if the version matches, and creates a new {@link ProtocolVersion}
131     * otherwise.
132     *
133     * @param major     the major version
134     * @param minor     the minor version
135     *
136     * @return  a protocol version with the same protocol name
137     *          and the argument version
138     */
139    public ProtocolVersion forVersion(int major, int minor) {
140
141        if ((major == this.major) && (minor == this.minor)) {
142            return this;
143        }
144
145        // argument checking is done in the constructor
146        return new ProtocolVersion(this.protocol, major, minor);
147    }
148
149
150    /**
151     * Obtains a hash code consistent with {@link #equals}.
152     *
153     * @return  the hashcode of this protocol version
154     */
155    public final int hashCode() {
156        return this.protocol.hashCode() ^ (this.major * 100000) ^ this.minor;
157    }
158
159
160    /**
161     * Checks equality of this protocol version with an object.
162     * The object is equal if it is a protocl version with the same
163     * protocol name, major version number, and minor version number.
164     * The specific class of the object is <i>not</i> relevant,
165     * instances of derived classes with identical attributes are
166     * equal to instances of the base class and vice versa.
167     *
168     * @param obj       the object to compare with
169     *
170     * @return  <code>true</code> if the argument is the same protocol version,
171     *          <code>false</code> otherwise
172     */
173    public final boolean equals(Object obj) {
174        if (this == obj) {
175            return true;
176        }
177        if (!(obj instanceof ProtocolVersion)) {
178            return false;
179        }
180        ProtocolVersion that = (ProtocolVersion) obj;
181
182        return ((this.protocol.equals(that.protocol)) &&
183                (this.major == that.major) &&
184                (this.minor == that.minor));
185    }
186
187
188    /**
189     * Checks whether this protocol can be compared to another one.
190     * Only protocol versions with the same protocol name can be
191     * {@link #compareToVersion compared}.
192     *
193     * @param that      the protocol version to consider
194     *
195     * @return  <code>true</code> if {@link #compareToVersion compareToVersion}
196     *          can be called with the argument, <code>false</code> otherwise
197     */
198    public boolean isComparable(ProtocolVersion that) {
199        return (that != null) && this.protocol.equals(that.protocol);
200    }
201
202
203    /**
204     * Compares this protocol version with another one.
205     * Only protocol versions with the same protocol name can be compared.
206     * This method does <i>not</i> define a total ordering, as it would be
207     * required for {@link java.lang.Comparable}.
208     *
209     * @param that      the protocl version to compare with
210     *
211     * @return   a negative integer, zero, or a positive integer
212     *           as this version is less than, equal to, or greater than
213     *           the argument version.
214     *
215     * @throws IllegalArgumentException
216     *         if the argument has a different protocol name than this object,
217     *         or if the argument is <code>null</code>
218     */
219    public int compareToVersion(ProtocolVersion that) {
220        if (that == null) {
221            throw new IllegalArgumentException
222                ("Protocol version must not be null.");
223        }
224        if (!this.protocol.equals(that.protocol)) {
225            throw new IllegalArgumentException
226                ("Versions for different protocols cannot be compared. " +
227                 this + " " + that);
228        }
229
230        int delta = getMajor() - that.getMajor();
231        if (delta == 0) {
232            delta = getMinor() - that.getMinor();
233        }
234        return delta;
235    }
236
237
238    /**
239     * Tests if this protocol version is greater or equal to the given one.
240     *
241     * @param version   the version against which to check this version
242     *
243     * @return  <code>true</code> if this protocol version is
244     *          {@link #isComparable comparable} to the argument
245     *          and {@link #compareToVersion compares} as greater or equal,
246     *          <code>false</code> otherwise
247     */
248    public final boolean greaterEquals(ProtocolVersion version) {
249        return isComparable(version) && (compareToVersion(version) >= 0);
250    }
251
252
253    /**
254     * Tests if this protocol version is less or equal to the given one.
255     *
256     * @param version   the version against which to check this version
257     *
258     * @return  <code>true</code> if this protocol version is
259     *          {@link #isComparable comparable} to the argument
260     *          and {@link #compareToVersion compares} as less or equal,
261     *          <code>false</code> otherwise
262     */
263    public final boolean lessEquals(ProtocolVersion version) {
264        return isComparable(version) && (compareToVersion(version) <= 0);
265    }
266
267
268    /**
269     * Converts this protocol version to a string.
270     *
271     * @return  a protocol version string, like "HTTP/1.1"
272     */
273    public String toString() {
274        CharArrayBuffer buffer = new CharArrayBuffer(16);
275        buffer.append(this.protocol);
276        buffer.append('/');
277        buffer.append(Integer.toString(this.major));
278        buffer.append('.');
279        buffer.append(Integer.toString(this.minor));
280        return buffer.toString();
281    }
282
283    public Object clone() throws CloneNotSupportedException {
284        return super.clone();
285    }
286
287}
288