michael@0: /* michael@0: * ==================================================================== michael@0: * Licensed to the Apache Software Foundation (ASF) under one michael@0: * or more contributor license agreements. See the NOTICE file michael@0: * distributed with this work for additional information michael@0: * regarding copyright ownership. The ASF licenses this file michael@0: * to you under the Apache License, Version 2.0 (the michael@0: * "License"); you may not use this file except in compliance michael@0: * with the License. You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, michael@0: * software distributed under the License is distributed on an michael@0: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY michael@0: * KIND, either express or implied. See the License for the michael@0: * specific language governing permissions and limitations michael@0: * under the License. michael@0: * ==================================================================== michael@0: * michael@0: * This software consists of voluntary contributions made by many michael@0: * individuals on behalf of the Apache Software Foundation. For more michael@0: * information on the Apache Software Foundation, please see michael@0: * . michael@0: * michael@0: */ michael@0: package ch.boye.httpclientandroidlib.conn.scheme; michael@0: michael@0: import java.util.Locale; michael@0: michael@0: import ch.boye.httpclientandroidlib.annotation.Immutable; michael@0: michael@0: import ch.boye.httpclientandroidlib.util.LangUtils; michael@0: michael@0: /** michael@0: * Encapsulates specifics of a protocol scheme such as "http" or "https". Schemes are identified michael@0: * by lowercase names. Supported schemes are typically collected in a {@link SchemeRegistry michael@0: * SchemeRegistry}. michael@0: *

michael@0: * For example, to configure support for "https://" URLs, you could write code like the following: michael@0: *

michael@0:  * Scheme https = new Scheme("https", 443, new MySecureSocketFactory());
michael@0:  * SchemeRegistry.DEFAULT.register(https);
michael@0:  * 
michael@0: * michael@0: * @since 4.0 michael@0: */ michael@0: @Immutable michael@0: public final class Scheme { michael@0: michael@0: /** The name of this scheme, in lowercase. (e.g. http, https) */ michael@0: private final String name; michael@0: michael@0: /** The socket factory for this scheme */ michael@0: private final SchemeSocketFactory socketFactory; michael@0: michael@0: /** The default port for this scheme */ michael@0: private final int defaultPort; michael@0: michael@0: /** Indicates whether this scheme allows for layered connections */ michael@0: private final boolean layered; michael@0: michael@0: /** A string representation, for {@link #toString toString}. */ michael@0: private String stringRep; michael@0: /* michael@0: * This is used to cache the result of the toString() method michael@0: * Since the method always generates the same value, there's no michael@0: * need to synchronize, and it does not affect immutability. michael@0: */ michael@0: michael@0: /** michael@0: * Creates a new scheme. michael@0: * Whether the created scheme allows for layered connections michael@0: * depends on the class of factory. michael@0: * michael@0: * @param name the scheme name, for example "http". michael@0: * The name will be converted to lowercase. michael@0: * @param port the default port for this scheme michael@0: * @param factory the factory for creating sockets for communication michael@0: * with this scheme michael@0: * michael@0: * @since 4.1 michael@0: */ michael@0: public Scheme(final String name, final int port, final SchemeSocketFactory factory) { michael@0: if (name == null) { michael@0: throw new IllegalArgumentException("Scheme name may not be null"); michael@0: } michael@0: if ((port <= 0) || (port > 0xffff)) { michael@0: throw new IllegalArgumentException("Port is invalid: " + port); michael@0: } michael@0: if (factory == null) { michael@0: throw new IllegalArgumentException("Socket factory may not be null"); michael@0: } michael@0: this.name = name.toLowerCase(Locale.ENGLISH); michael@0: this.socketFactory = factory; michael@0: this.defaultPort = port; michael@0: this.layered = factory instanceof LayeredSchemeSocketFactory; michael@0: } michael@0: michael@0: /** michael@0: * Creates a new scheme. michael@0: * Whether the created scheme allows for layered connections michael@0: * depends on the class of factory. michael@0: * michael@0: * @param name the scheme name, for example "http". michael@0: * The name will be converted to lowercase. michael@0: * @param factory the factory for creating sockets for communication michael@0: * with this scheme michael@0: * @param port the default port for this scheme michael@0: * michael@0: * @deprecated Use {@link #Scheme(String, int, SchemeSocketFactory)} michael@0: */ michael@0: @Deprecated michael@0: public Scheme(final String name, michael@0: final SocketFactory factory, michael@0: final int port) { michael@0: michael@0: if (name == null) { michael@0: throw new IllegalArgumentException michael@0: ("Scheme name may not be null"); michael@0: } michael@0: if (factory == null) { michael@0: throw new IllegalArgumentException michael@0: ("Socket factory may not be null"); michael@0: } michael@0: if ((port <= 0) || (port > 0xffff)) { michael@0: throw new IllegalArgumentException michael@0: ("Port is invalid: " + port); michael@0: } michael@0: michael@0: this.name = name.toLowerCase(Locale.ENGLISH); michael@0: if (factory instanceof LayeredSocketFactory) { michael@0: this.socketFactory = new LayeredSchemeSocketFactoryAdaptor( michael@0: (LayeredSocketFactory) factory); michael@0: this.layered = true; michael@0: } else { michael@0: this.socketFactory = new SchemeSocketFactoryAdaptor(factory); michael@0: this.layered = false; michael@0: } michael@0: this.defaultPort = port; michael@0: } michael@0: michael@0: /** michael@0: * Obtains the default port. michael@0: * michael@0: * @return the default port for this scheme michael@0: */ michael@0: public final int getDefaultPort() { michael@0: return defaultPort; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Obtains the socket factory. michael@0: * If this scheme is {@link #isLayered layered}, the factory implements michael@0: * {@link LayeredSocketFactory LayeredSocketFactory}. michael@0: * michael@0: * @return the socket factory for this scheme michael@0: * michael@0: * @deprecated Use {@link #getSchemeSocketFactory()} michael@0: */ michael@0: @Deprecated michael@0: public final SocketFactory getSocketFactory() { michael@0: if (this.socketFactory instanceof SchemeSocketFactoryAdaptor) { michael@0: return ((SchemeSocketFactoryAdaptor) this.socketFactory).getFactory(); michael@0: } else { michael@0: if (this.layered) { michael@0: return new LayeredSocketFactoryAdaptor( michael@0: (LayeredSchemeSocketFactory) this.socketFactory); michael@0: } else { michael@0: return new SocketFactoryAdaptor(this.socketFactory); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Obtains the socket factory. michael@0: * If this scheme is {@link #isLayered layered}, the factory implements michael@0: * {@link LayeredSocketFactory LayeredSchemeSocketFactory}. michael@0: * michael@0: * @return the socket factory for this scheme michael@0: * michael@0: * @since 4.1 michael@0: */ michael@0: public final SchemeSocketFactory getSchemeSocketFactory() { michael@0: return this.socketFactory; michael@0: } michael@0: michael@0: /** michael@0: * Obtains the scheme name. michael@0: * michael@0: * @return the name of this scheme, in lowercase michael@0: */ michael@0: public final String getName() { michael@0: return name; michael@0: } michael@0: michael@0: /** michael@0: * Indicates whether this scheme allows for layered connections. michael@0: * michael@0: * @return true if layered connections are possible, michael@0: * false otherwise michael@0: */ michael@0: public final boolean isLayered() { michael@0: return layered; michael@0: } michael@0: michael@0: /** michael@0: * Resolves the correct port for this scheme. michael@0: * Returns the given port if it is valid, the default port otherwise. michael@0: * michael@0: * @param port the port to be resolved, michael@0: * a negative number to obtain the default port michael@0: * michael@0: * @return the given port or the defaultPort michael@0: */ michael@0: public final int resolvePort(int port) { michael@0: return port <= 0 ? defaultPort : port; michael@0: } michael@0: michael@0: /** michael@0: * Return a string representation of this object. michael@0: * michael@0: * @return a human-readable string description of this scheme michael@0: */ michael@0: @Override michael@0: public final String toString() { michael@0: if (stringRep == null) { michael@0: StringBuilder buffer = new StringBuilder(); michael@0: buffer.append(this.name); michael@0: buffer.append(':'); michael@0: buffer.append(Integer.toString(this.defaultPort)); michael@0: stringRep = buffer.toString(); michael@0: } michael@0: return stringRep; michael@0: } michael@0: michael@0: @Override michael@0: public final boolean equals(Object obj) { michael@0: if (this == obj) return true; michael@0: if (obj instanceof Scheme) { michael@0: Scheme that = (Scheme) obj; michael@0: return this.name.equals(that.name) michael@0: && this.defaultPort == that.defaultPort michael@0: && this.layered == that.layered; michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public int hashCode() { michael@0: int hash = LangUtils.HASH_SEED; michael@0: hash = LangUtils.hashCode(hash, this.defaultPort); michael@0: hash = LangUtils.hashCode(hash, this.name); michael@0: hash = LangUtils.hashCode(hash, this.layered); michael@0: return hash; michael@0: } michael@0: michael@0: }