001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether; 020 021import java.util.Arrays; 022 023import static java.util.Objects.requireNonNull; 024 025/** 026 * A helper class to create keys, to be used with {@link SessionData} and {@link RepositoryCache} instances as keys. 027 * Resolver codebase started with {@link String} keys, that are generally perfect for keys in these constructs, as 028 * long as they are used by components loaded once in system. As we saw, some subsystems like transports can be 029 * loaded multiple times. For example, in case of Maven, some transport may be present in Maven core, but also loaded up 030 * by some extension. In this case, key should distinguish between their {@link ClassLoader}s. 031 * <p> 032 * It is this class caller responsibility to use proper objects as keys, this class merely helps one to create composite 033 * keys out of object instances that are expected to be anyway good candidates as key. Examples of these objects are 034 * {@link String} instances but also {@link Class} instances, and many other types also usable as keys. 035 * <p> 036 * Important: never forget to perform {@code null}-check, when getting cache from {@link RepositorySystemSession#getCache()} 037 * method as it may return {@code null}, when cache is disabled session-wise. 038 * <p> 039 * Historical note: As mentioned above, use of {@link String} instances for keys is perfect match, and it worked from 040 * very start. But, as use cases got more and more complex and keys used started to be constructed in more and more 041 * sophisticated ways, but were still {@link String} instances. Believe, or not, the sole purpose of string keys 042 * was easier debugging. By using this helper class, this convenience should remain. 043 * 044 * @see RepositorySystemSession#getData() 045 * @see SessionData 046 * @see RepositorySystemSession#getCache() 047 * @see RepositoryCache 048 * @since 2.0.19 049 */ 050public final class Keys { 051 private Keys() {} 052 053 /** 054 * Creates object instance usable as key in session data and cache. Objects passed to this method may or may 055 * not implement equals/hashCode, but it is responsibility of caller to understand what is she or he doing. 056 * <p> 057 * If the first element of key elements is an object instance that was created by this method, creation of 058 * "subkeys" happens, where original key elements expanded from first element and are concatenated with new ones. 059 * This expansion happens <em>only, if the first key element was already a key created by this class</em>. 060 * If the arguments contain only one argument, and it is an object instance that was created by this method, 061 * the passed in instance is returned unmodified. 062 * <p> 063 * Based on what kind of elements are used, one can create multiple kind of keys: 064 * <ul> 065 * <li>To create <em>globally matched keys</em>, preferred is to use {@link String} key elements</li> 066 * <li>To create <em>ClassLoader wide matched keys</em>, make sure at least one key element is {@link Class} that needs to be scoped to ClassLoader</li> 067 * <li>To create <em>private, matched by creator only keys</em>, make sure to have one key element, that requires instance equality matching, and there is no other same instance of element</li> 068 * </ul> 069 * 070 * @param keys The key elements, it may not be {@code null} and may not have zero elements. 071 * @return An object instance usable as key. 072 */ 073 public static Object of(Object... keys) { 074 requireNonNull(keys, "keys cannot be null"); 075 if (keys.length == 0) { 076 throw new IllegalArgumentException("keys must have at least one element"); 077 } else if (keys[0] instanceof Key) { 078 Key head = (Key) keys[0]; 079 if (keys.length == 1) { 080 return head; 081 } else { 082 return new Key(concat(head.keys, Arrays.copyOfRange(keys, 1, keys.length))); 083 } 084 } else { 085 return new Key(keys); 086 } 087 } 088 089 private static final class Key { 090 private final Object[] keys; 091 private final int hashCode; 092 093 private Key(Object[] keys) { 094 this.keys = keys.clone(); 095 this.hashCode = Arrays.hashCode(keys); 096 } 097 098 @Override 099 public boolean equals(Object obj) { 100 if (this == obj) { 101 return true; 102 } else if (!(obj instanceof Key)) { 103 return false; 104 } 105 Key that = (Key) obj; 106 return Arrays.equals(keys, that.keys); 107 } 108 109 @Override 110 public int hashCode() { 111 return hashCode; 112 } 113 114 @Override 115 public String toString() { 116 return Arrays.toString(keys); 117 } 118 } 119 120 private static Object[] concat(Object[] one, Object[] two) { 121 Object[] result = Arrays.copyOf(one, one.length + two.length); 122 System.arraycopy(two, 0, result, one.length, two.length); 123 return result; 124 } 125}