1 /* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.caliper.util; 18 19 import com.google.common.collect.ImmutableBiMap; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import com.google.common.collect.Maps; 23 import com.google.common.io.ByteSource; 24 import com.google.common.io.Closer; 25 import com.google.common.io.Resources; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.lang.reflect.Member; 30 import java.lang.reflect.Modifier; 31 import java.util.Map; 32 import java.util.Properties; 33 import java.util.Set; 34 import java.util.concurrent.CountDownLatch; 35 import java.util.concurrent.TimeUnit; 36 37 public final class Util { Util()38 private Util() {} 39 40 // Users have no idea that nested classes are identified with '$', not '.', so if class lookup 41 // fails try replacing the last . with $. lenientClassForName(String className)42 public static Class<?> lenientClassForName(String className) throws ClassNotFoundException { 43 try { 44 return loadClass(className); 45 } catch (ClassNotFoundException ignored) { 46 // try replacing the last dot with a $, in case that helps 47 // example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1 48 // amusingly, the $ character means three different things in this one line alone 49 String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1"); 50 return loadClass(newName); 51 } 52 } 53 54 /** 55 * Search for a class by name. 56 * 57 * @param className the name of the class. 58 * @return the class. 59 * @throws ClassNotFoundException if the class could not be found. 60 */ loadClass(String className)61 public static Class<?> loadClass(String className) throws ClassNotFoundException { 62 // Use the thread context class loader. This is necessary because in some configurations, e.g. 63 // when run from a single JAR containing caliper and all its dependencies the caliper JAR 64 // ends up on the boot class path of the Worker and so needs to the use thread context class 65 // loader to load classes provided by the user. 66 return Class.forName(className, true, Thread.currentThread().getContextClassLoader()); 67 } 68 loadProperties(ByteSource is)69 public static ImmutableMap<String, String> loadProperties(ByteSource is) throws IOException { 70 Properties props = new Properties(); 71 Closer closer = Closer.create(); 72 InputStream in = closer.register(is.openStream()); 73 try { 74 props.load(in); 75 } finally { 76 closer.close(); 77 } 78 return Maps.fromProperties(props); 79 } 80 resourceSupplier(final Class<?> c, final String name)81 public static ByteSource resourceSupplier(final Class<?> c, final String name) { 82 return Resources.asByteSource(c.getResource(name)); 83 } 84 prefixedSubmap( Map<String, T> props, String prefix)85 private static <T> ImmutableMap<String, T> prefixedSubmap( 86 Map<String, T> props, String prefix) { 87 ImmutableMap.Builder<String, T> submapBuilder = ImmutableMap.builder(); 88 for (Map.Entry<String, T> entry : props.entrySet()) { 89 String name = entry.getKey(); 90 if (name.startsWith(prefix)) { 91 submapBuilder.put(name.substring(prefix.length()), entry.getValue()); 92 } 93 } 94 return submapBuilder.build(); 95 } 96 97 /** 98 * Returns a map containing only those entries whose key starts with {@code <groupName>.}. 99 * 100 * <p>The keys in the returned map have had their {@code <groupName>.} prefix removed. 101 * 102 * <p>e.g. If given a map that contained {@code group.key1 -> value1, key2 -> value2} and a 103 * {@code groupName} of {@code group} it would produce a map containing {@code key1 -> value1}. 104 */ subgroupMap( Map<String, String> map, String groupName)105 public static ImmutableMap<String, String> subgroupMap( 106 Map<String, String> map, String groupName) { 107 return prefixedSubmap(map, groupName + "."); 108 } 109 isPublic(Member member)110 public static boolean isPublic(Member member) { 111 return Modifier.isPublic(member.getModifiers()); 112 } 113 isStatic(Member member)114 public static boolean isStatic(Member member) { 115 return Modifier.isStatic(member.getModifiers()); 116 } 117 118 private static final long FORCE_GC_TIMEOUT_SECS = 2; 119 forceGc()120 public static void forceGc() { 121 System.gc(); 122 System.runFinalization(); 123 final CountDownLatch latch = new CountDownLatch(1); 124 new Object() { 125 @Override protected void finalize() { 126 latch.countDown(); 127 } 128 }; 129 System.gc(); 130 System.runFinalization(); 131 try { 132 latch.await(FORCE_GC_TIMEOUT_SECS, TimeUnit.SECONDS); 133 } catch (InterruptedException e) { 134 Thread.currentThread().interrupt(); 135 } 136 } 137 assignNames(Set<T> items)138 public static <T> ImmutableBiMap<T, String> assignNames(Set<T> items) { 139 ImmutableList<T> itemList = ImmutableList.copyOf(items); 140 ImmutableBiMap.Builder<T, String> itemNamesBuilder = ImmutableBiMap.builder(); 141 for (int i = 0; i < itemList.size(); i++) { 142 itemNamesBuilder.put(itemList.get(i), generateUniqueName(i)); 143 } 144 return itemNamesBuilder.build(); 145 } 146 generateUniqueName(int index)147 private static String generateUniqueName(int index) { 148 if (index < 26) { 149 return String.valueOf((char) ('A' + index)); 150 } else { 151 return generateUniqueName(index / 26 - 1) + generateUniqueName(index % 26); 152 } 153 } 154 } 155