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 private MonotonicTreeMap<String, Constructor<? extends ExternalConstructor<?>>> parsers = new MonotonicTreeMap<>(); 051 052 @SuppressWarnings({"unchecked", "rawtypes"}) 053 public void addParser(final Class<? extends ExternalConstructor<?>> ec) throws BindException { 054 final 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(final Class<U> clazz, final Class<? extends ExternalConstructor<T>> ec) 060 throws BindException { 061 final Constructor<? extends ExternalConstructor<T>> c; 062 try { 063 c = ec.getDeclaredConstructor(String.class); 064 c.setAccessible(true); 065 } catch (final NoSuchMethodException e) { 066 throw new BindException("Constructor " 067 + ReflectionUtilities.getFullName(ec) + "(String) does not exist!", e); 068 } 069 c.setAccessible(true); 070 parsers.put(ReflectionUtilities.getFullName(clazz), c); 071 } 072 073 public void mergeIn(final ParameterParser p) { 074 for (final String s : p.parsers.keySet()) { 075 if (!parsers.containsKey(s)) { 076 parsers.put(s, p.parsers.get(s)); 077 } else { 078 if (!parsers.get(s).equals(p.parsers.get(s))) { 079 throw new IllegalArgumentException( 080 "Conflict detected when merging parameter parsers! To parse " + s 081 + " I have a: " + ReflectionUtilities.getFullName(parsers.get(s).getDeclaringClass()) 082 + " the other instance has a: " 083 + ReflectionUtilities.getFullName(p.parsers.get(s).getDeclaringClass())); 084 } 085 } 086 } 087 } 088 089 public <T> T parse(final Class<T> c, final String s) { 090 final Class<?> d = ReflectionUtilities.boxClass(c); 091 for (final Type e : ReflectionUtilities.classAndAncestors(d)) { 092 final String name = ReflectionUtilities.getFullName(e); 093 if (parsers.containsKey(name)) { 094 final T ret = parse(name, s); 095 if (c.isAssignableFrom(ret.getClass())) { 096 return ret; 097 } else { 098 throw new ClassCastException("Cannot cast from " + ret.getClass() + " to " + c); 099 } 100 } 101 } 102 return parse(ReflectionUtilities.getFullName(d), s); 103 } 104 105 @SuppressWarnings("unchecked") 106 public <T> T parse(final String name, final String value) { 107 if (parsers.containsKey(name)) { 108 try { 109 return (T) (parsers.get(name).newInstance(value).newInstance()); 110 } catch (final ReflectiveOperationException e) { 111 throw new IllegalArgumentException("Error invoking constructor for " 112 + name, e); 113 } 114 } else { 115 if (name.equals(String.class.getName())) { 116 return (T) value; 117 } 118 if (name.equals(Byte.class.getName())) { 119 return (T) (Byte) Byte.parseByte(value); 120 } 121 if (name.equals(Character.class.getName())) { 122 return (T) (Character) value.charAt(0); 123 } 124 if (name.equals(Short.class.getName())) { 125 return (T) (Short) Short.parseShort(value); 126 } 127 if (name.equals(Integer.class.getName())) { 128 return (T) (Integer) Integer.parseInt(value); 129 } 130 if (name.equals(Long.class.getName())) { 131 return (T) (Long) Long.parseLong(value); 132 } 133 if (name.equals(Float.class.getName())) { 134 return (T) (Float) Float.parseFloat(value); 135 } 136 if (name.equals(Double.class.getName())) { 137 return (T) (Double) Double.parseDouble(value); 138 } 139 if (name.equals(Boolean.class.getName())) { 140 return (T) (Boolean) Boolean.parseBoolean(value); 141 } 142 if (name.equals(Void.class.getName())) { 143 throw new ClassCastException("Can't instantiate void"); 144 } 145 throw new UnsupportedOperationException("Don't know how to parse a " + name); 146 } 147 } 148 149 public boolean canParse(final String name) { 150 return parsers.containsKey(name) || BUILTIN_NAMES.contains(name); 151 } 152}