usr/src/java/rad/org/opensolaris/os/rad/ADRName.java
author Stephen Talley <stephen.talley@oracle.com>
Mon, 28 Mar 2011 10:53:34 -0400
changeset 685 767674b0a2fb
parent 604 20d9acfeb7fb
child 686 3ead80dd78ec
permissions -rw-r--r--
18094 s/StringBuffer/StringBuilder/g

/*
 * 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) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
 */

package org.opensolaris.os.rad;

import java.util.*;
import java.util.logging.*;
import javax.management.*;

public class ADRName {
    private String domain_;
    private TreeMap<String, String> kvs_ = new TreeMap<String, String>();

    /* Wire-ordered keys to play jconsole's silly collation game */
    private List<String> keys_ = new ArrayList<String>();

    public ADRName(ObjectName on) {
	domain_ = on.getDomain();
	Hashtable<String, String> keys = on.getKeyPropertyList();
	for (Map.Entry<String, String> e : keys.entrySet()) {
	    String value = e.getValue();
	    try {
		value = ObjectName.unquote(value);
	    } catch (IllegalArgumentException ex) {
	    }
	    kvs_.put(e.getKey(), value);
	}
    }

    public ADRName(String domain, String[] keys, String[] values) {
	domain_ = domain;

	if (keys != null && values != null) {
	    if (keys.length != values.length)
		throw new IllegalArgumentException(
		    "Unequal number of keys and values");
	} else if (keys != null || values != null) {
	    throw new IllegalArgumentException(
		"Only one of keys and values specified");
	}

	for (int i = 0; i < keys.length; i++)
	    kvs_.put(keys[i], values[i]);
    }

    public ADRName(String str) {
	String[] pieces = str.split(":", 2);
	domain_ = pieces[0];

	if (pieces.length != 2)
	    throw new IllegalArgumentException("No keys/values: " + str);
	int pos, start = 0, dst = 0;
	boolean inkey = true;
	char[] c = pieces[1].toCharArray();
	String key = null;
	char[] scratch = new char[str.length()];
	for (pos = 0; pos < c.length; pos++) {
	    char ochar = c[pos];
	    if (ochar == ',') {
		if (pos == start || inkey)
		    throw new IllegalArgumentException("Empty value: " + str);
		kvs_.put(key, new String(scratch, 0, dst));
		keys_.add(key);
		dst = 0;
		inkey = true;
		start = pos + 1;
		continue;
	    } else if (ochar == '=') {
		if (pos == start || !inkey)
		    throw new IllegalArgumentException("Empty key: " + str);
		key = new String(scratch, 0, dst);
		dst = 0;
		inkey = false;
		start = pos + 1;
		continue;
	    } else if (ochar == '\\') {
		if (++pos >= c.length)
		    throw new IllegalArgumentException("Ends with \\: " + str);
		switch (c[pos]) {
		case 'S':
		    ochar = '\\';
		    break;
		case 'E':
		    ochar = '=';
		    break;
		case 'C':
		    ochar = ',';
		    break;
		default:
		    throw new IllegalArgumentException("Bad sequence: " +
			ochar + c[pos]);
		}
	    }
	    scratch[dst++] = ochar;
        }
	if (pos == start || inkey)
	    throw new IllegalArgumentException("Empty value: " + str);
	kvs_.put(key, new String(scratch, 0, dst));
	keys_.add(key);
    }

    public ObjectName toObjectName() throws MalformedObjectNameException {
	/*
	 * In theory, ObjectName keys are unordered.  In practice, their
	 * order is used as a hint to improve the appearance of jconsole.
	 * Discordant as this is, the end result is actually pretty nice.
	 *
	 * Unfortunately, the only ObjectName constructor that chooses
	 * practice over theory is the one that takes the string form, so
	 * below we cons up a String so that ObjectName(String) can then
	 * immediately undo our work and parse it.
	 */
	StringBuilder sb = new StringBuilder(domain_).append(':');
	boolean first = true;
	for (String key : keys_) {
	    if (first)
		first = false;
	    else
		sb.append(',');
	    sb.append(key).append('=').append(ObjectName.quote(kvs_.get(key)));
	}
	return (new ObjectName(sb.toString()));
    }

    @Override
    public String toString() {
	StringBuilder sb = new StringBuilder();
	sb.append(domain_);
	char sep = ':';
	for (Map.Entry<String, String> e : kvs_.entrySet()) {
	    sb.append(sep);
	    quote(sb, e.getKey());
	    sb.append('=');
	    quote(sb, e.getValue());
	    sep = ',';
	}

	return (sb.toString());
    }

    @Override
    public boolean equals(Object o) {
	if (!(o instanceof ADRName))
	    return false;

	ADRName a = (ADRName)o;
	return domain_.equals(a.domain_) && kvs_.equals(a.kvs_);
    }

    @Override
    public int hashCode() {
	int hash = 7;
	hash = 53 * hash + (domain_ != null ? domain_.hashCode() : 0);
	hash = 53 * hash + (kvs_ != null ? kvs_.hashCode() : 0);
	return hash;
    }

    private static void quote(StringBuilder sb, String s) {
	char[] sarray = s.toCharArray();
	char[] c = new char[s.length() * 2];
	int to, from;
	for (to = 0, from = 0; from < sarray.length; from++) {
	    char ochar = sarray[from];
	    switch (ochar) {
	    case '\\':
		ochar = 'S';
		c[to++] = '\\';
		break;
	    case '=':
		ochar = 'E';
		c[to++] = '\\';
		break;
	    case ',':
		ochar = 'C';
		c[to++] = '\\';
		break;
	    }
	    c[to++] = ochar;
	}
	sb.append(c, 0, to);
    }

    public static void main(String[] argv) {
	ADRName n = new ADRName("foo.bar:name=value");
	System.out.println("Name: " + n);
	try {
	    System.out.println("ObjectName: " + n.toObjectName().toString());
	    System.out.println("UnObjectName: " +
		new ADRName(n.toObjectName()));
	} catch (MalformedObjectNameException ex) {
	    Logger.getLogger(ADRName.class.getName()).log(
		Level.SEVERE, null, ex);
	}
    }
}