root/usr/src/lib/libslp/javalib/com/sun/slp/ServiceType.java
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 *
 */

//  ServiceType.java: Model a service type.
//  Author:           James Kempf
//  Created On:       Thu Apr  9 09:23:18 1998
//  Last Modified By: James Kempf
//  Last Modified On: Mon Oct 19 15:43:18 1998
//  Update Count:     33
//

package com.sun.slp;

import java.util.*;
import java.io.*;


/**
 * The ServiceType class conceals the complex details of whether a
 * service type name is a simple URL scheme identifier, a service:
 * type, an abstract type or protocol type.
 *
 * @author James Kempf
 */

public class ServiceType extends Object implements Serializable {

    boolean isServiceURL = true;        // was it originally a service: type?
    private String type1 = "";  // scheme, abstract, or protocol.
    private String type2 = "";  // concrete, if type was abstract.
    private String na = "";     // naming authority.

    // For removing IANA.

    static final String IANA = "iana";

    /**
     * Create a service type object from the type name. The name may
     * take the form of any valid SLP service type name.
     *
     * @param t The type name.
     * @return The ServiceType object.
     * @exception IllegalArgumentException If the name is syntactically
     *                                   incorrect.
     */

    public ServiceType(String t) throws IllegalArgumentException {

        parse(t);

    }

    /**
     * Return true if type name was from a service: URL.
     *
     * @return True if type name came from service: URL.
     */

    public boolean isServiceURL() {
        return isServiceURL;

    }

    /**
     * Return true if type name is for an abstract type.
     *
     * @return True if type name is for an abstract type.
     */

    public boolean isAbstractType() {
        return (type2.length() > 0);

    }

    /**
     * Return true if naming authority is default.
     *
     * @return True if naming authority is default.
     */

    public boolean isNADefault() {
        return (na.length() <= 0);

    }

    /**
     * Return the concrete service type name without naming authority.
     *
     * @return concrete type name.
     */

    public String getConcreteTypeName() {
        return type2;

    }

    /**
     * Return the principle type name, which is either the abstract
     * type name or the protocol name, without naming authority.
     *
     * @return Principle type name.
     */

    public String getPrincipleTypeName() {
        return type1;

    }

    /**
     * Return the fully formatted abstract type name, if it is an abstract
     * type, otherwise the empty string.
     */

    public String getAbstractTypeName() {
        if (isAbstractType()) {
            return "service:" + type1 + (na.length() > 0 ? ("." + na):"");

        }
        return "";

    }

    /**
     * Return the naming authority name.
     *
     * @return Naming authority name.
     */

    public String getNamingAuthority() {
        return na;

    }

    /**
     *Validate a naming authority name.
     */

    static void validateTypeComponent(String name)
        throws ServiceLocationException {

        validateTypeComponentInternal(name, false);
    }

    // Validate, allowing '.' if allowDot is true.

    static private void
        validateTypeComponentInternal(String name, boolean allowDot)
        throws ServiceLocationException {
        int i, n = name.length();

        for (i = 0; i < n; i++) {
            char c = name.charAt(i);

            if ((Character.isLetterOrDigit(c) == false) &&
                (c != '+') && (c != '-')) {
                boolean throwIt = true;

                // If dot is allowed, don't throw it.

                if (allowDot && (c == '.')) {
                    throwIt = false;

                }

                if (throwIt) {
                    throw
                        new IllegalArgumentException(
        SLPConfig.getSLPConfig().formatMessage("service_type_syntax",
                                               new Object[] {name}));
                }
            }
        }
    }

    // Two service type names are equal if they have the same
    //  types, naming authority, and same service: flag.

    public boolean equals(Object o) {

        if (o == this) {
            return true;

        }

        if (!(o instanceof ServiceType)) {
            return false;

        }

        ServiceType type = (ServiceType)o;

        return
            (isServiceURL == type.isServiceURL) &&
            type1.equals(type.type1) &&
            type2.equals(type.type2) &&
            na.equals(type.na);

    }

    // Format the service type name for output.

    public String toString() {

        return
            (isServiceURL ? "service:" : "") +
            type1 +
            (na.length() > 0 ? ("." + na) : "") +
            (type2.length() > 0 ? (":" + type2) : "");

    }

    // Return a hash code for the type.

    public int hashCode() {

        return type1.hashCode() +
            na.hashCode() +
            type2.hashCode() +
            (isServiceURL ? Defaults.SERVICE_PREFIX.hashCode():0);

    }

    // Parse a service type name with optional naming authority.

    private void parse(String t) {
        StringTokenizer st = new StringTokenizer(t, ":.", true);

        try {

            // This loop is a kludgy way to break out of the parse so
            //  we only throw at one location in the code.

            do {

                String tok = st.nextToken();

                if (tok.equals(":") || tok.equals(".")) {
                    break;  // error!

                }

                // Look for a nonservice: URL.

                if (!tok.equalsIgnoreCase(Defaults.SERVICE_PREFIX)) {
                    isServiceURL = false;

                    // Need to eat through all dots.

                    do {
                        type1 = type1 + tok.toLowerCase();

                        // Break when we run out of tokens.

                        if (!st.hasMoreTokens()) {
                            break;

                        }

                        tok = st.nextToken();

                    } while (true);

                    // Check for disallowed characters. Allow '.'.

                    validateTypeComponentInternal(type1, true);

                    // There should be no more tokens.

                    if (st.hasMoreTokens()) {
                        break; // error!

                    }

                    return; // done!

                }

                tok = st.nextToken();

                if (!tok.equals(":")) {
                    break; // error!

                }

                // Get the protocol or abstract type name.

                type1 = st.nextToken().toLowerCase();

                validateTypeComponent(type1);

                // From here on in, everything is optional, so be sure
                //  to check for no remaining tokens.

                if (!st.hasMoreTokens()) {
                    return;
                // done! It's a simple protocol type w.o. naming authority.

                }

                // It's either got a naming authority or is an abstract
                //  type (or both).

                tok = st.nextToken();

                // Check for naming authorithy.

                if (tok.equals(".")) {
                    tok = st.nextToken();

                    validateTypeComponent(tok);

                    if (!tok.equalsIgnoreCase(IANA)) {

                        na = tok.toLowerCase();

                    } else {

                        // Error to have IANA.

                        break;

                    }

                    if (!st.hasMoreTokens()) {
                        return;
                // done! It's a simple protocol type w. naming authority.

                    }

                    tok = st.nextToken();

                }

                // Should be at the separator to concrete type.

                if (!tok.equals(":")) {
                    break; // error!

                }

                tok = st.nextToken();

                // This is the concrete type name.

                validateTypeComponent(tok);

                type2 = tok.toLowerCase();

                // Should be no more tokens.

                if (st.hasMoreTokens()) {
                    break; // error!

                }

                return; // done!

            } while (false);

        } catch (NoSuchElementException ex) {
            throw
                new IllegalArgumentException(
                SLPConfig.getSLPConfig().formatMessage("service_type_syntax",
                                                       new Object[] {t}));

        } catch (ServiceLocationException ex) {
            throw
                new IllegalArgumentException(ex.getMessage());

        }

        throw
            new IllegalArgumentException(
                SLPConfig.getSLPConfig().formatMessage("service_type_syntax",
                                                       new Object[] {t}));

    }

}