/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.groups; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.NoSuchElementException; import javax.naming.InvalidNameException; import javax.naming.Name; import javax.naming.NamingException; import org.apereo.portal.EntityIdentifier; /** * A composite key and type that uniquely identify a portal entity. The composite key contains a * service name, which may be compound, and a native key, which is the key that identifies the * entity in the local service. * */ public class CompositeEntityIdentifier extends EntityIdentifier implements IGroupConstants { private static final long serialVersionUID = 1L; // static vars: protected static final String separator; private static final LoadingCache<String, Name> NAME_PARSE_CACHE = CacheBuilder.newBuilder() .maximumSize(1000) .build( new CacheLoader<String, Name>() { @Override public Name load(String s) throws Exception { int start = 0; int separatorLength = separator.length(); int end = s.indexOf(separator, start); List<String> list = new ArrayList<String>(4); while (end != -1) { list.add(s.substring(start, end)); start = end + separatorLength; end = s.indexOf(separator, start); } list.add(s.substring(start)); return new CompositeEntityIdentifier.NameImpl(list); } }); static { String sep; try { sep = GroupServiceConfiguration.getConfiguration().getNodeSeparator(); } catch (Exception ex) { sep = IGroupConstants.NODE_SEPARATOR; } separator = sep; } // instance vars: protected Name compositeKey; protected String cachedCompositeKey; protected String cachedLocalKey; protected Name cachedServiceName; /** * @param entityKey java.lang.String * @param entityType java.lang.Class */ public CompositeEntityIdentifier(String entityKey, Class entityType) throws GroupsException { super(entityKey, entityType); try { compositeKey = parseCompoundKey(entityKey); } catch (NamingException ne) { throw new GroupsException("Error in group key", ne); } } /** @return javax.naming.Name */ protected synchronized Name getCompositeKey() { return compositeKey; } /** @return java.lang.String */ public synchronized String getKey() { if (cachedCompositeKey == null) { cachedCompositeKey = getCompositeKey().toString(); } return cachedCompositeKey; } /** @return java.lang.String */ public synchronized String getLocalKey() { if (cachedLocalKey == null) { cachedLocalKey = getCompositeKey().get(size() - 1); } return cachedLocalKey; } /** * If the composite key is either empty or has a single node, there is no service name. * * @return javax.naming.Name */ public synchronized Name getServiceName() { if (size() < 2) { return null; } if (cachedServiceName == null) { cachedServiceName = getCompositeKey().getPrefix(size() - 1); } return cachedServiceName; } /** Returns a new empty Name */ public Name newName() throws InvalidNameException { return new NameImpl(); } /** @param newCompositeKey javax.naming.Name */ public synchronized void setCompositeKey(Name newCompositeKey) { compositeKey = newCompositeKey; cachedCompositeKey = null; cachedLocalKey = null; cachedServiceName = null; } /** @param newServiceName javax.naming.Name */ public void setServiceName(Name newServiceName) throws InvalidNameException { Name newKey = newName().addAll(newServiceName).add(getLocalKey()); setCompositeKey(newKey); cachedServiceName = newServiceName; } /** @return int */ protected int size() { return getCompositeKey().size(); } /** * Returns a String that represents the value of this object. * * @return java.lang.String */ public String toString() { return "CompositeEntityIdentifier (" + type + "(" + getKey() + "))"; } /** Returns a CompoundName parsed from key */ public Name parseCompoundKey(String key) throws NamingException { return NAME_PARSE_CACHE.getUnchecked(key); } private static final class NameImpl implements Name { List<String> components; public NameImpl() { this(new ArrayList(4)); } public NameImpl(List<String> comps) { super(); components = comps; } public Name add(String comp) { components.add(comp); return this; } public Name add(int posn, String comp) { components.add(posn, comp); return this; } public Name addAll(int posn, Name n) { int i = posn; for (Enumeration e = n.getAll(); e.hasMoreElements(); i++) { add(i, (String) e.nextElement()); } return this; } public Name addAll(Name n) { for (Enumeration e = n.getAll(); e.hasMoreElements(); ) { add((String) e.nextElement()); } return this; } public Object clone() { List<String> comps = (List<String>) ((ArrayList<String>) components).clone(); return new NameImpl(comps); } public int compareTo(Object obj) { if (this == obj) { return 0; } if (!(obj instanceof Name)) { throw new ClassCastException("Not a Name"); } Name name = (Name) obj; int len1 = size(); int len2 = name.size(); int n = Math.min(len1, len2); int index1 = 0, index2 = 0; while (n-- != 0) { String comp1 = get(index1++); String comp2 = name.get(index2++); comp1 = comp1.trim(); comp2 = comp2.trim(); int local = comp1.compareTo(comp2); if (local != 0) { return local; } } return len1 - len2; } public boolean endsWith(Name n) { int startIndex = size() - n.size(); if (startIndex < 0 || startIndex > size()) { return false; } Enumeration suffix = n.getAll(); try { Enumeration mycomps = getAll(); while (mycomps.hasMoreElements()) { String my = (String) mycomps.nextElement(); String his = (String) suffix.nextElement(); my = my.trim(); his = his.trim(); if (!(my.equals(his))) { return false; } } } catch (NoSuchElementException e) { return false; } return true; } public String get(int posn) { return components.get(posn); } public Enumeration getAll() { return new NameImplEnumerator(components, 0, components.size()); } public Name getPrefix(int posn) { if (posn < 0 || posn >= size()) { throw new ArrayIndexOutOfBoundsException(posn); } return getNameComponents(0, posn); } public Name getSuffix(int posn) { if (posn < 0 || posn > size()) { throw new ArrayIndexOutOfBoundsException(posn); } return getNameComponents(posn, size()); } public boolean isEmpty() { return (components.isEmpty()); } public Object remove(int posn) throws InvalidNameException { if (posn < 0 || posn >= size()) { throw new InvalidNameException("Invalid position."); } return components.remove(posn); } public int size() { return (components.size()); } public boolean startsWith(Name n) { Name myPrefix = getPrefix(n.size()); return (myPrefix.compareTo(n) == 0); } public String toString() { if (size() == 0) { return ""; } if (size() == 1) { return get(0); } // TODO: for jdk 1.5: // StringBuilder sb = new StringBuilder(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < size(); i++) { if (i != 0) { sb.append(separator); } sb.append(get(i)); } return (sb.toString()); } public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof Name)) { return false; } Name target = (Name) obj; if (target.size() != this.size()) { return false; } // For our purposes this is sufficient, if not entirely correct: return target.toString().equals(this.toString()); } public int hashCode() { int hash = 0; for (Enumeration e = getAll(); e.hasMoreElements(); ) { String comp = (String) e.nextElement(); hash += comp.hashCode(); } return hash; } private Enumeration<String> getComponents(int start, int limit) { return new NameImplEnumerator(components, start, limit); } private Name getNameComponents(int start, int limit) { List<String> comps = new ArrayList<String>(limit - start); for (Enumeration<String> e = getComponents(start, limit); e.hasMoreElements(); ) { comps.add(e.nextElement()); } return new NameImpl(comps); } } private static final class NameImplEnumerator implements Enumeration<String> { List<String> list; int count; int limit; NameImplEnumerator(List<String> l, int start, int lim) { list = l; count = start; limit = lim; } public boolean hasMoreElements() { return count < limit; } public String nextElement() { if (count < limit) { return list.get(count++); } throw new NoSuchElementException("NameImplEnumerator"); } } }