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.apache.reef.tang.formats; 020 021import org.apache.reef.tang.ExternalConstructor; 022import org.apache.reef.tang.exceptions.BindException; 023import org.apache.reef.tang.util.MonotonicTreeMap; 024import org.apache.reef.tang.util.ReflectionUtilities; 025 026import java.lang.reflect.Constructor; 027import java.lang.reflect.Type; 028import java.util.Collections; 029import java.util.HashSet; 030import java.util.Set; 031 032public class ParameterParser { 033 private static final Set<String> BUILTIN_NAMES = new HashSet<String>() { 034 private static final long serialVersionUID = 1L; 035 036 { 037 Collections.addAll(this, 038 String.class.getName(), 039 Byte.class.getName(), 040 Character.class.getName(), 041 Short.class.getName(), 042 Integer.class.getName(), 043 Long.class.getName(), 044 Float.class.getName(), 045 Double.class.getName(), 046 Boolean.class.getName(), 047 Void.class.getName()); 048 } 049 }; 050 MonotonicTreeMap<String, Constructor<? extends ExternalConstructor<?>>> parsers = new MonotonicTreeMap<>(); 051 052 @SuppressWarnings({"unchecked", "rawtypes"}) 053 public void addParser(Class<? extends ExternalConstructor<?>> ec) throws BindException { 054 Class<?> tc = (Class<?>) ReflectionUtilities.getInterfaceTarget( 055 ExternalConstructor.class, ec); 056 addParser((Class) tc, (Class) ec); 057 } 058 059 public <T, U extends T> void addParser(Class<U> clazz, Class<? extends ExternalConstructor<T>> ec) throws BindException { 060 Constructor<? extends ExternalConstructor<T>> c; 061 try { 062 c = ec.getDeclaredConstructor(String.class); 063 c.setAccessible(true); 064 } catch (NoSuchMethodException e) { 065 throw new BindException("Constructor " 066 + ReflectionUtilities.getFullName(ec) + "(String) does not exist!", e); 067 } 068 c.setAccessible(true); 069 parsers.put(ReflectionUtilities.getFullName(clazz), c); 070 } 071 072 public void mergeIn(ParameterParser p) { 073 for (String s : p.parsers.keySet()) { 074 if (!parsers.containsKey(s)) { 075 parsers.put(s, p.parsers.get(s)); 076 } else { 077 if (!parsers.get(s).equals(p.parsers.get(s))) { 078 throw new IllegalArgumentException( 079 "Conflict detected when merging parameter parsers! To parse " + s 080 + " I have a: " + ReflectionUtilities.getFullName(parsers.get(s).getDeclaringClass()) 081 + " the other instance has a: " + ReflectionUtilities.getFullName(p.parsers.get(s).getDeclaringClass())); 082 } 083 } 084 } 085 } 086 087 public <T> T parse(Class<T> c, String s) { 088 Class<?> d = ReflectionUtilities.boxClass(c); 089 for (Type e : ReflectionUtilities.classAndAncestors(d)) { 090 String name = ReflectionUtilities.getFullName(e); 091 if (parsers.containsKey(name)) { 092 T ret = parse(name, s); 093 if (c.isAssignableFrom(ret.getClass())) { 094 return ret; 095 } else { 096 throw new ClassCastException("Cannot cast from " + ret.getClass() + " to " + c); 097 } 098 } 099 } 100 return parse(ReflectionUtilities.getFullName(d), s); 101 } 102 103 @SuppressWarnings("unchecked") 104 public <T> T parse(String name, String value) { 105 if (parsers.containsKey(name)) { 106 try { 107 return (T) (parsers.get(name).newInstance(value).newInstance()); 108 } catch (ReflectiveOperationException e) { 109 throw new IllegalArgumentException("Error invoking constructor for " 110 + name, e); 111 } 112 } else { 113 if (name.equals(String.class.getName())) { 114 return (T) value; 115 } 116 if (name.equals(Byte.class.getName())) { 117 return (T) (Byte) Byte.parseByte(value); 118 } 119 if (name.equals(Character.class.getName())) { 120 return (T) (Character) value.charAt(0); 121 } 122 if (name.equals(Short.class.getName())) { 123 return (T) (Short) Short.parseShort(value); 124 } 125 if (name.equals(Integer.class.getName())) { 126 return (T) (Integer) Integer.parseInt(value); 127 } 128 if (name.equals(Long.class.getName())) { 129 return (T) (Long) Long.parseLong(value); 130 } 131 if (name.equals(Float.class.getName())) { 132 return (T) (Float) Float.parseFloat(value); 133 } 134 if (name.equals(Double.class.getName())) { 135 return (T) (Double) Double.parseDouble(value); 136 } 137 if (name.equals(Boolean.class.getName())) { 138 return (T) (Boolean) Boolean.parseBoolean(value); 139 } 140 if (name.equals(Void.class.getName())) { 141 throw new ClassCastException("Can't instantiate void"); 142 } 143 throw new UnsupportedOperationException("Don't know how to parse a " + name); 144 } 145 } 146 147 public boolean canParse(String name) { 148 return parsers.containsKey(name) || BUILTIN_NAMES.contains(name); 149 } 150}