001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014 ForgeRock AS. 015 */ 016 017package org.forgerock.openig.heap; 018 019import static java.lang.String.*; 020import static org.forgerock.util.Reject.*; 021 022import org.forgerock.util.Reject; 023 024/** 025 * A Name uniquely identify an object within a hierarchy. 026 * It is composed of a (possible {@code null} parent Name) and a leaf name (never {@code null}). 027 * <p> 028 * Consumers of that API are free to do their own Name rendering (they have access to the whole Name's chain 029 * with {@link #getParent()} method) or use the pre-defined {@link #getFullyQualifiedName()} 030 * and {@link #getScopedName()} methods. Theses methods use the plus ({@literal +}) character as separator. 031 * <p> 032 * The Name instances are immutable. 033 */ 034public final class Name { 035 036 /** 037 * Builds a new Name using the given name parts. 038 * They are ordered in descending order (ancestors first, leaf last) 039 * 040 * @param parts 041 * ordered fragments of the name 042 * @return a new Name using the given name parts. 043 */ 044 public static Name of(final String... parts) { 045 Reject.ifTrue(parts.length == 0); 046 Name name = null; 047 for (String part : parts) { 048 name = new Name(name, part); 049 } 050 return name; 051 } 052 053 /** 054 * Builds a new Name for the given type. 055 * The generated name will use the given type's short name as leaf and will have no parent. 056 * 057 * @param type 058 * typ used to generate a name 059 * @return a new Name for the given type 060 */ 061 public static Name of(final Class<?> type) { 062 return Name.of(type.getSimpleName()); 063 } 064 065 private final Name parent; 066 private final String leaf; 067 068 /** 069 * Builds a new hierarchical Name with the given {@code parent} Name and the given {@code leaf} leaf name. Notice 070 * that the parent name can be {@code null} while the leaf part cannot be {@code null} (this is verified and a 071 * NullPointerException is thrown if it is). 072 * 073 * @param parent 074 * parent Name 075 * @param leaf 076 * leaf name (cannot be {@code null}) 077 */ 078 private Name(final Name parent, final String leaf) { 079 this.parent = parent; 080 this.leaf = checkNotNull(leaf); 081 } 082 083 /** 084 * Returns the parent Name (can be {@code null}). 085 * 086 * @return the parent Name (can be {@code null}). 087 */ 088 public Name getParent() { 089 return parent; 090 } 091 092 /** 093 * Returns the leaf name (cannot be {@code null}). 094 * 095 * @return the leaf name. 096 */ 097 public String getLeaf() { 098 return leaf; 099 } 100 101 /** 102 * Creates a new Name, relative to this Name with the given leaf name. 103 * 104 * @param name 105 * relative leaf name 106 * @return a new Name, relative to this Name. 107 */ 108 public Name child(final String name) { 109 return new Name(this, name); 110 } 111 112 /** 113 * Returns this name with the last segment adapted to include the decorator name. 114 * The last segment is changed to follow this pattern: {@literal @decorator[last-segment]}. 115 * 116 * @param decorator 117 * decorator name. 118 * @return a new decorated name based on this name 119 */ 120 public Name decorated(final String decorator) { 121 return new Name(parent, format("@%s[%s]", decorator, leaf)); 122 } 123 124 /** 125 * Returns a String representation of this Name that includes the full Name hierarchy. 126 * <p> 127 * The following format has to be expected: 128 * <pre> 129 * {@code 130 * (parent '+')* leaf 131 * } 132 * </pre> 133 * <p> 134 * Examples: 135 * <ul> 136 * <li>{@code LocalNameOnly}</li> 137 * <li>{@code /openig/config/config.json+_Router}</li> 138 * <li>{@code /openig/config/config.json+_Router+/openig/config/routes/openid-connect.json+OAuth2Filter}</li> 139 * </ul> 140 * 141 * @return a String representation of this Name that includes the full Name hierarchy. 142 */ 143 public String getFullyQualifiedName() { 144 StringBuilder sb = new StringBuilder(); 145 if (parent != null) { 146 sb.append(parent.getFullyQualifiedName()); 147 sb.append("+"); 148 } 149 sb.append(leaf); 150 return sb.toString(); 151 } 152 153 /** 154 * Returns a String representation of this Name that includes only the first parent and the leaf name. 155 * <p> 156 * The following format has to be expected: 157 * <pre> 158 * {@code 159 * (parent '+')? leaf 160 * } 161 * </pre> 162 * <p> 163 * Examples: 164 * <ul> 165 * <li>{@code LocalNameOnly}</li> 166 * <li>{@code /openig/config/config.json+_Router}</li> 167 * </ul> 168 * 169 * @return a String representation of this Name that includes only the first parent and the leaf name. 170 */ 171 public String getScopedName() { 172 StringBuilder sb = new StringBuilder(); 173 if (parent != null) { 174 sb.append(parent.getLeaf()); 175 sb.append("+"); 176 } 177 sb.append(leaf); 178 return sb.toString(); 179 } 180 181 @Override 182 public boolean equals(final Object o) { 183 if (this == o) { 184 return true; 185 } 186 if (!(o instanceof Name)) { 187 return false; 188 } 189 190 Name name = (Name) o; 191 192 if (!leaf.equals(name.leaf)) { 193 return false; 194 } 195 if (parent == null) { 196 return name.parent == null; 197 } 198 return parent.equals(name.parent); 199 } 200 201 @Override 202 public int hashCode() { 203 int result = parent != null ? parent.hashCode() : 0; 204 result = 31 * result + leaf.hashCode(); 205 return result; 206 } 207 208 /** 209 * Returns the fully qualified name of this Name (format: {@literal (parent '+')* leaf}). 210 * 211 * @return the fully qualified name of this Name. 212 * @see #getFullyQualifiedName() 213 */ 214 @Override 215 public String toString() { 216 return getFullyQualifiedName(); 217 } 218}