All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.jandex.DotName Maven / Gradle / Ivy

There is a newer version: 3.3.8
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2013 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.jandex;

/**
 * A DotName represents a dot separated name, typically a Java package or a Java class.
 * It has two possible variants. A simple wrapper based variant allows for fast construction
 * (it simply wraps the specified name string). Whereas, a componentized variant represents
 * one or more String methodInternal that when combined with a dot character, assemble the full
 * name. The intention of the componentized variant is that the String methodInternal can be reused
 * to offer memory efficiency. This reuse is common in Java where packages and classes follow
 * a tree structure.
 *
 * 

Both the simple and componentized variants are considered semantically equivalent if they * refer to the same logical name. More specifically the equals and hashCode methods return the * same values for the same semantic name regardless of the variant used. Which variant to use * when depends on the specific performance and overhead objectives of the specific use pattern. * *

Simple names are cheap to construct (just a an additional wrapper object), so are ideal for * temporary use, like looking for an entry in a Map. Componentized names however require that * they be split in advance, and so require some additional time to construct. However the memory * benefits of reusing component strings make them desirable when stored in a longer term area * such as in a Java data structure. * * @author Jason T. Greene * */ public final class DotName implements Comparable { static final DotName JAVA_NAME; static final DotName JAVA_LANG_NAME; static final DotName OBJECT_NAME; private final DotName prefix; private final String local; private int hash; private final boolean componentized; private final boolean innerClass; static { JAVA_NAME = new DotName(null, "java", true, false); JAVA_LANG_NAME = new DotName(JAVA_NAME, "lang", true, false); OBJECT_NAME = new DotName(JAVA_LANG_NAME, "Object", true, false); } /** * Constructs a simple DotName which stores the string in it's entirety. This variant is ideal * for temporary usage, such as looking up an entry in a Map. * * @param name A fully qualified non-null name (with dots) * @return a simple DotName that wraps name */ public static DotName createSimple(String name) { return new DotName(null, name, false, false); } /** * Constructs a componentized DotName. Each DotName refers to a parent * prefix (or null if there is no further prefix) in addition to a local * name that has no dot separator. The fully qualified name this DotName * represents is constructed by recursing all parent prefixes and joining all * local name values with the '.' character. * * @param prefix Another DotName that is the portion to the left of * localName, this may be null if there is not one * @param localName the local non-null portion of this name, which does not contain * '.' * @return a componentized DotName. */ public static DotName createComponentized(DotName prefix, String localName) { if (localName.indexOf('.') != -1) throw new IllegalArgumentException("A componentized DotName can not contain '.' characters in a local name"); return new DotName(prefix, localName, true, false); } /** * Constructs a componentized DotName. Each DotName refers to a parent * prefix (or null if there is no further prefix) in addition to a local * name that has no dot separator. The fully qualified name this DotName * represents is consructed by recursing all parent prefixes and joining all * local name values with the '.' character. * * @param prefix Another DotName that is the portion to the left of * localName, this may be null if there is not one * @param localName the local non-null portion of this name, which does not contain * '.' * @param innerClass whether or not this localName is an inner class style name, requiring '$' vs '.' * @return a componentized DotName. */ public static DotName createComponentized(DotName prefix, String localName, boolean innerClass) { if (localName.indexOf('.') != -1) throw new IllegalArgumentException("A componentized DotName can not contain '.' characters in a local name"); return new DotName(prefix, localName, true, innerClass); } DotName(DotName prefix, String local, boolean noDots, boolean innerClass) { if (local == null) { throw new IllegalArgumentException("Local string can not be null"); } if (prefix != null && !prefix.componentized) { throw new IllegalArgumentException("A componentized DotName must have a componentized prefix, or null"); } this.prefix = prefix; this.local = local; this.componentized = noDots; this.innerClass = innerClass; } /** * Returns the parent prefix for this DotName or null if there is none. * Simple DotName variants never have a prefix. * * @return the parent prefix for this DotName */ public DotName prefix() { return prefix; } /** * Returns the local portion of this DotName. In simple variants, the entire * fully qualified string is returned. In componentized variants, just the * right most portion not including a separator (either . or $) is returned. * *

Use {@link #withoutPackagePrefix()} instead of this method if the * desired value is simply the right most portion (including dollar signs if * present) after a '.' delimiter. *

* * @return the non-null local portion of this DotName */ public String local() { return local; } /** * Returns the portion of this DotName that does not contain a package prefix. * In the case of an inner class syntax name, the $ portion is included in * the return value. * * @return the portion of the name that is not package prefixed * @since 2.1.1 */ public String withoutPackagePrefix() { if (componentized) { StringBuilder builder = new StringBuilder(); stripPackage(builder); return builder.toString(); } else { int index = local.lastIndexOf('.'); return index == -1 ? local : index < local.length() - 1 ? local.substring(index + 1) : ""; } } private void stripPackage(StringBuilder builder) { if (innerClass) { prefix.stripPackage(builder); builder.append('$'); } builder.append(local); } /** * Returns whether this DotName is a componentized variant. * * @return true if it is componentized, false if it is a simple DotName */ public boolean isComponentized() { return componentized; } /** * Returns whether the local portion of a componentized DotName is separated * by an inner class style delimiter ('$"). This should not be used to test * whether the name truly refers to an inner class, only that the dollar * sign delimits the value. Java class names are allowed to contain dollar * signs, so the local value could simply be a fragment of a class name, and * not an actual inner class. The correct way to determine whether or not a * name refers to an actual inner class is to lookup a ClassInfo in the * index and call and examine the nesting type like so: * *
     *    index.get(name).nestingType() != TOP_LEVEL;
     * 
* * * @return true if local is an inner class style delimited name, false otherwise */ public boolean isInner() { return innerClass; } /** * Returns the regular fully qualifier class name. * * @return The fully qualified class name */ public String toString() { return toString('.'); } public String toString(char delim) { String string = local; if (prefix != null) { StringBuilder builder = new StringBuilder(); buildString(delim, builder); string = builder.toString(); } return string; } private void buildString(char delim, StringBuilder builder) { if (prefix != null) { prefix.buildString(delim, builder); builder.append(innerClass ? '$' : delim); } builder.append(local); } /** * Returns a hash code which is based on the semantic representation of this DotName. * Whether or not a DotName is componentized has no impact on the calculated hash code. * * @return a hash code representing this object * @see Object#hashCode() */ public int hashCode() { int hash = this.hash; if (hash != 0) return hash; if (prefix != null) { hash = prefix.hashCode() * 31 + (innerClass ? '$' : '.'); // Luckily String.hashCode documents the algorithm it follows for (int i = 0; i < local.length(); i++) { hash = 31 * hash + local.charAt(i); } } else { hash = local.hashCode(); } return this.hash = hash; } /** * Compares a DotName to another DotName and returns whether this DotName * is lesser than, greater than, or equal to the specified DotName. If this DotName is lesser, * a negative value is returned. If greater, a positive value is returned. If equal, zero is returned. * * @param other the DotName to compare to * @return a negative number if this is less than the specified object, a positive if greater, and zero if equal * * @see Comparable#compareTo(Object) */ @Override public int compareTo(DotName other) { IndexState s1 = new IndexState(); IndexState s2 = new IndexState(); for (;;) { int c1 = nextChar(s1, this); int c2 = nextChar(s2, other); if (c1 == -1) { return c2 == -1 ? 0 : -1; } if (c2 == -1) { return 1; } if (c1 != c2) { return c1 - c2; } } } /** * Compares a DotName to another DotName and returns true if the represent * the same underlying semantic name. In other words, whether or not a * name is componentized or simple has no bearing on the comparison. * * @param o the DotName object to compare to * @return true if equal, false if not * * @see Object#equals(Object) */ public boolean equals(Object o) { if (this == o) return true; if (! (o instanceof DotName)) return false; DotName other = (DotName)o; if (other.prefix == null && prefix == null) return local.equals(other.local) && innerClass == other.innerClass; if (!other.componentized && componentized) return crossEquals(other, this); if (other.componentized && !componentized) return crossEquals(this, other); return prefix != null && innerClass == other.innerClass && local.equals(other.local) && prefix.equals(other.prefix); } private static boolean crossEquals(final DotName simple, final DotName comp) { final String exactToMatch = simple.local; // We start matching from the end, as that's what we have in componentized mode: int cursor = 0; final int len = exactToMatch.length(); for (DotName d = comp; d != null && cursor - 1 <= len; d = d.prefix) { cursor += 1 + d.local.length(); } if (--cursor != len) { return false; } DotName current = comp; while (current!=null) { final String nextFragment = current.local; final int fragLength = nextFragment.length(); if (! exactToMatch.regionMatches(cursor-fragLength, nextFragment, 0, fragLength)) { return false; } //Jump by fragment match, +1 for the separator symbol: cursor = cursor - fragLength - 1; if (cursor==-1) { //Our exactToMatch reference is finished; just verify we consumed comp completely as well:: return current.prefix == null; } final char expectNext = current.innerClass ? '$' : '.'; if (exactToMatch.charAt(cursor) != expectNext) { return false; } current=current.prefix; } //And finally, verify we consumed it all: return cursor == -1; } private static class IndexState { DotName currentPrefix; int offset; } private int nextChar(IndexState state, DotName name) { if (state.offset == -1) { return -1; } if (!name.componentized) { if (state.offset > name.local.length() - 1) { state.offset = -1; return -1; } return name.local.charAt(state.offset++); } DotName p = name, n = name; while (n.prefix != state.currentPrefix) { p = n; n = n.prefix; } if (state.offset > n.local.length() - 1) { if (n == name) { state.offset = -1; return -1; } else { state.offset = 0; state.currentPrefix = n; return p.isInner() ? '$' : '.'; } } return n.local.charAt(state.offset++); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy