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

org.eclipse.internal.xtend.util.QualifiedName Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.internal.xtend.util;

import java.lang.ref.WeakReference;
import java.util.List;

/**
 * A memory efficient structure to store qualified names composed of String segments which are (optionally) separated by a delimiter. QualifiedNames
 * must be constructed through one its create methods. The structure uses String pooling to store the single segments. Further it maintains a pool of
 * QualifiedName instances itself, so that each QualifiedName exists only once.
 * 
 * @author Karsten Thoms
 */
public final class QualifiedName {
	protected final int hash;
	protected String[] segments;
	protected WeakReference toString;

	protected static final QualifiedNameCache CACHE = new QualifiedNameCache();

	/**
	 * Constructor
	 * 
	 * @param segments
	 *            Segments of the Identifier.
	 * @param delimiter
	 *            A delimiter string for the construction of a String representation with {@link #toString()}. Can be null.
	 */
	protected QualifiedName(final String[] segments, final int hashCode) {
		this.segments = segments;
		hash = hashCode;
	}

	/**
	 * Append another Identifier to this instance
	 * 
	 * @param t
	 *            An identifier.
	 * @return Will return this extended by the segments of t
	 */
	public QualifiedName append(final QualifiedName t) {
		return CACHE.intern(segments, t.segments);
	}

	/**
	 * Returns a canonical String representation of this.
	 */
	@Override
	public String toString() {
		int segmentCount = segments.length;
		switch (segmentCount) {
		case 0:
			return "";
		case 1:
			return segments[0];
		default:
			String result = toString != null ? toString.get() : null;
			if (result == null) {
				StringBuilder builder = new StringBuilder(segmentCount * 8);
				for (String segment : segments) {
					builder.append(segment);
				}
				result = builder.toString();
				toString = new WeakReference(result);
			}
			return result;
		}
	}

	/**
	 * Returns a canonical String representation of this using the specified delimiter. This method will perform less than {@link #toString()}, since
	 * the result cannot be cached.
	 * 
	 * @param delimiter
	 *            A delimiter to insert between segments.
	 */
	public String toString(final String delimiter) {
		int segmentCount = segments.length;
		if (segmentCount < 2) {
			return toString();
		}
		// at least 2 segments
		final StringBuilder builder = new StringBuilder(segmentCount * (8 + delimiter.length()));
		for (String segment : segments) {
			if (segment != segments[0]) {
				builder.append(delimiter);
			}
			builder.append(segment);
		}
		return builder.toString();
	}

	public int getSegmentCount() {
		return segments.length;
	}

	public String getSegment(final int index) {
		return segments[index];
	}

	public String getLastSegment() {
		return segments[segments.length - 1];
	}

	public String getFirstSegment() {
		return segments[0];
	}

	/**
	 * Note that there's no override of {@link Object#equals(Object)} because all instances are interned and therefore there's at most one possible
	 * instance for sequence of segments.
	 */
	@Override
	public int hashCode() {
		return hash;
	}

	private static class QualifiedNameCache extends WeakInterningHashSet {
		private static final long serialVersionUID = 1L;

		protected int hashCode(final int initialHashCode, final String[] segments) {
			int length = segments.length;
			if (length == 0) {
				return 0;
			}
			int hashCode = initialHashCode;
			for (String segment : segments) {
				hashCode = (31 * hashCode) + segment.hashCode();
			}
			return hashCode;
		}

		@Override
		protected boolean equals(final Object object, final Object otherObject) {
			return object == otherObject;
		}

		protected int hashCode(final String[] segments) {
			return hashCode(1, segments);
		}

		protected int hashCode(final String[] segments1, final String[] segments2) {
			return hashCode(hashCode(segments1), segments2);
		}

		public QualifiedName intern(final String[] segments1, final String[] segments2) {
			int length1 = segments1.length;
			int length2 = segments2.length;
			int length = length1 + length2;
			int hashCode = hashCode(segments1, segments2);
			LOOP: for (Entry entry = getEntry(hashCode); entry != null; entry = entry.getNextEntry()) {
				QualifiedName qualifiedName = entry.get();
				if (qualifiedName != null) {
					String[] segments = qualifiedName.segments;
					if (length != segments.length) {
						break;
					}
					int i = 0;
					for (; i < length1; ++i) {
						if (segments[i] != segments1[i]) {
							continue LOOP;
						}
					}
					for (int j = 0; j < length2; ++i, ++j) {
						if (segments[i] != segments2[j]) {
							continue LOOP;
						}
					}
					return qualifiedName;
				}
			}

			String[] newSegments = new String[length];
			System.arraycopy(segments1, 0, newSegments, 0, length1);
			System.arraycopy(segments2, 0, newSegments, length1, length2);
			QualifiedName qualifiedName = new QualifiedName(newSegments, hashCode);
			addEntry(createEntry(qualifiedName, hashCode));
			return qualifiedName;
		}

		public QualifiedName intern(final String segment) {
			int hashCode = 31 + segment.hashCode();
			for (Entry entry = getEntry(hashCode); entry != null; entry = entry.getNextEntry()) {
				QualifiedName qualifiedName = entry.get();
				if (qualifiedName != null) {
					String[] segments = qualifiedName.segments;
					if ((segments.length == 1) && segments[0].equals(segment)) {
						return qualifiedName;
					}
				}
			}

			QualifiedName qualifiedName = new QualifiedName(new String[] { StringCache.get(segment) }, hashCode);
			addEntry(createEntry(qualifiedName, hashCode));
			return qualifiedName;
		}

		public QualifiedName intern(final String[] segments) {
			int hashCode = hashCode(segments);
			for (Entry entry = getEntry(hashCode); entry != null; entry = entry.getNextEntry()) {
				QualifiedName qualifiedName = entry.get();
				if (qualifiedName != null) {
					if (equals(qualifiedName.segments, segments)) {
						return qualifiedName;
					}
				}
			}

			for (int i = 0; i < segments.length; i++) {
				segments[i] = StringCache.get(segments[i]);
			}

			QualifiedName qualifiedName = new QualifiedName(segments, hashCode);
			addEntry(createEntry(qualifiedName, hashCode));
			return qualifiedName;
		}

		private boolean equals(final String[] segments1, final String[] segments2) {
			int length = segments1.length;
			if (segments2.length != length) {
				return false;
			}

			for (int i = 0; i < length; i++) {
				if (!segments1[i].equals(segments2[i])) {
					return false;
				}
			}

			return true;
		}
	}

	/**
	 * Factory method.
	 * 
	 * @param segments
	 *            segments the segments of the to-be-created qualified name.
	 * @return a {@link QualifiedName}. When the factory method was already invoked with the same arguments the same instance as the previous call
	 *         will be returned.
	 */
	public static QualifiedName create(final String[] segments) {
		return CACHE.intern(segments);
	}

	/**
	 * Factory method. A name with a single segment.
	 * 
	 * @param segment
	 *            The string segment of the qualified name.
	 * @return a {@link QualifiedName}. When the factory method was already invoked with the same arguments the same instance as the previous call
	 *         will be returned.
	 */
	public static QualifiedName create(final String segment) {
		return CACHE.intern(segment);
	}

	/**
	 * Factory method. A name separated by a delimiter.
	 * 
	 * @param segments
	 *            The string segment of the qualified name.
	 * @param delimiter
	 *            The delimiter which separates the segments.
	 * @return a {@link QualifiedName}. When the factory method was already invoked with the same arguments the same instance as the previous call
	 *         will be returned.
	 */
	public static QualifiedName create(final String segments, final String delimiter) {
		final List segmentList = StringHelper.split(segments, delimiter);
		if (segmentList.size() == 1) { // only simple name
			return CACHE.intern(segments);
		}
		final String[] segmented = segmentList.toArray(new String[segmentList.size()]);
		return CACHE.intern(segmented);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy