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