/* * Copyright 2011 Google Inc. * * Licensed 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 com.google.web.bindery.requestfactory.vm.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Provides access to payload deobfuscation services for server and JVM-based * clients. The deobfuscation data is baked into GWT-based clients by the * generator. */ public class Deobfuscator { /** * Creates Deobfuscators. */ public static class Builder { /** * Load a pre-computed Builder from the classpath. The builder * implementation is expected to have been generated by the annotation * processor as part of the build process. * * @see com.google.web.bindery.requestfactory.apt.DeobfuscatorBuilder * @see com.google.web.bindery.requestfactory.server.ResolverServiceLayer */ public static Builder load(Class<?> clazz, ClassLoader resolveClassesWith) { Throwable ex; try { Class<?> found; try { // Used by the server found = Class.forName(clazz.getName() + GENERATED_SUFFIX, false, resolveClassesWith); } catch (ClassNotFoundException ignored) { // Used by JRE-only clients found = Class.forName(clazz.getName() + GENERATED_SUFFIX_LITE, false, resolveClassesWith); } Class<? extends Builder> builderClass = found.asSubclass(Builder.class); Builder builder = builderClass.newInstance(); return builder; } catch (ClassNotFoundException e) { throw new RuntimeException("The RequestFactory ValidationTool must be run for the " + clazz.getCanonicalName() + " RequestFactory type"); } catch (InstantiationException e) { ex = e; } catch (IllegalAccessException e) { ex = e; } throw new RuntimeException(ex); } private Deobfuscator d = new Deobfuscator(); { d.domainToClientType = new HashMap<String, List<String>>(); d.operationData = new HashMap<OperationKey, OperationData>(); d.typeTokens = new HashMap<String, String>(); } public Deobfuscator build() { Deobfuscator toReturn = d; toReturn.domainToClientType = Collections.unmodifiableMap(toReturn.domainToClientType); toReturn.operationData = Collections.unmodifiableMap(toReturn.operationData); toReturn.referencedTypes = Collections.unmodifiableSet(new HashSet<String>(toReturn.typeTokens.values())); toReturn.typeTokens = Collections.unmodifiableMap(toReturn.typeTokens); d = null; return toReturn; } public Builder merge(Deobfuscator existing) { d.domainToClientType.putAll(existing.domainToClientType); d.operationData.putAll(existing.operationData); // referencedTypes recomputed in build() d.typeTokens.putAll(existing.typeTokens); return this; } public Builder withClientToDomainMappings(String domainBinaryName, List<String> value) { List<String> clientBinaryNames; switch (value.size()) { case 0: clientBinaryNames = Collections.emptyList(); break; case 1: clientBinaryNames = Collections.singletonList(value.get(0)); break; default: clientBinaryNames = Collections.unmodifiableList(new ArrayList<String>(value)); } d.domainToClientType.put(domainBinaryName, clientBinaryNames); return this; } public Builder withOperation(OperationKey key, OperationData data) { d.operationData.put(key, data); return this; } public Builder withRawTypeToken(String token, String binaryName) { d.typeTokens.put(token, binaryName); return this; } } private static final String GENERATED_SUFFIX = "DeobfuscatorBuilder"; private static final String GENERATED_SUFFIX_LITE = GENERATED_SUFFIX + "Lite"; /** * Maps domain types (e.g Foo) to client proxy types (e.g. FooAProxy, * FooBProxy). */ private Map<String, List<String>> domainToClientType; private Map<OperationKey, OperationData> operationData; private Set<String> referencedTypes; /** * Map of obfuscated ids to binary class names. */ private Map<String, String> typeTokens; Deobfuscator() { } /** * Returns the client proxy types whose {@code @ProxyFor} is exactly * {@code binaryTypeName}. Ordered such that the most-derived types will be * iterated over first. */ public List<String> getClientProxies(String binaryTypeName) { return domainToClientType.get(binaryTypeName); } /** * Returns a method descriptor that should be invoked on the service object. */ public String getDomainMethodDescriptor(String operation) { OperationData data = getData(operation); return data == null ? null : data.getDomainMethodDescriptor(); } public String getRequestContext(String operation) { OperationData data = getData(operation); return data == null ? null : data.getRequestContext(); } public String getRequestContextMethodDescriptor(String operation) { OperationData data = getData(operation); return data == null ? null : data.getClientMethodDescriptor(); } public String getRequestContextMethodName(String operation) { OperationData data = getData(operation); return data == null ? null : data.getMethodName(); } /** * Returns a type's binary name based on an obfuscated token. */ public String getTypeFromToken(String token) { return typeTokens.get(token); } public boolean isReferencedType(String name) { return referencedTypes.contains(name); } private OperationData getData(String operation) { OperationData data = operationData.get(new OperationKey(operation)); return data; } }