001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.xbean.propertyeditor; 018 019import java.lang.reflect.Method; 020import java.lang.reflect.Modifier; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.List; 025 026/** 027 * Of the javax and java packages in the Java 8 JVM, there are roughly 028 * 10 static factory patterns in use. 029 * 030 * Here they are listed in the order they are preferred by this library 031 * 032 * 64 valueOf 033 * 7 new 034 * 6 decode 035 * 5 for 036 * 4 of 037 * 1 parse 038 * 1 from 039 * 1 create 040 * 1 compile 041 * 40 get 042 * 043 * Though get* has the second most usage in the JVM, it is also the least 044 * consistent and in classes that have multiple factories, it is the least 045 * preferred. 046 * 047 * For each of these prefixes there is a sub order of preference, using 048 * "create" as an example, this is the preferred usage: 049 * 050 * - create 051 * - create<Type> 052 * - create* 053 * 054 */ 055public class StaticFactoryConverter extends AbstractConverter { 056 057 private final Method method; 058 059 public StaticFactoryConverter(final Class type, final Method method) { 060 super(type); 061 this.method = method; 062 } 063 064 @Override 065 protected Object toObjectImpl(final String text) { 066 try { 067 return method.invoke(null, text); 068 } catch (final Exception e) { 069 final String message = String.format("Cannot convert string '%s' to %s.", text, super.getType()); 070 throw new PropertyEditorException(message, e); 071 } 072 } 073 074 public static StaticFactoryConverter editor(final Class type) { 075 final List<Method> candidates = getCandidates(type); 076 077 if (candidates.size() == 0) return null; 078 079 final Method method = select(candidates); 080 081 return new StaticFactoryConverter(type, method); 082 } 083 084 static List<Method> getCandidates(final Class type) { 085 final List<Method> candidates = new ArrayList<Method>(); 086 087 for (final Method method : type.getMethods()) { 088 if (!Modifier.isStatic(method.getModifiers())) continue; 089 if (!Modifier.isPublic(method.getModifiers())) continue; 090 if (!method.getReturnType().equals(type)) continue; 091 if (method.getParameterTypes().length != 1) continue; 092 if (!method.getParameterTypes()[0].equals(String.class)) continue; 093 094 candidates.add(method); 095 } 096 097 return candidates; 098 } 099 100 /** 101 * We want the selection to be stable and not dependent on 102 * VM reflection ordering. 103 */ 104 static Method select(final List<Method> candidates) { 105 sort(candidates); 106 107 return candidates.get(0); 108 } 109 110 static void sort(final List<Method> candidates) { 111 Collections.sort(candidates, new Comparator<Method>() { 112 public int compare(final Method a, final Method b) { 113 int av = grade(a); 114 int bv = grade(b); 115 return (a.getName().compareTo(b.getName()) + (av - bv)); 116 } 117 }); 118 } 119 120 private static int grade(final Method a) { 121 final String type = a.getReturnType().getSimpleName(); 122 final String name = a.getName(); 123 124 // valueOf beats all 125 if (name.equals("valueOf")) return -990000; 126 if (name.equals("valueOf" + type)) return -980000; 127 if (name.startsWith("valueOf")) return -970000; 128 129 // new* 130 if (name.equals("new" + type)) return -890000; 131 if (name.equals("newInstance")) return -880000; 132 if (name.startsWith("new")) return -870000; 133 134 // decode* 135 if (name.equals("decode")) return -790000; 136 if (name.equals("decode" + type)) return -780000; 137 if (name.startsWith("decode")) return -770000; 138 139 // for* 140 if (name.equals("for" + type)) return -690000; 141 if (name.startsWith("for")) return -680000; 142 143 // of* 144 if (name.equals("of")) return -590000; 145 if (name.equals("of" + type)) return -580000; 146 if (name.startsWith("of")) return -570000; 147 148 // parse* 149 if (name.equals("parse")) return -490000; 150 if (name.equals("parse" + type)) return -480000; 151 if (name.startsWith("parse")) return -470000; 152 153 // from* 154 if (name.equals("from")) return -390000; 155 if (name.equals("fromString")) return -380000; 156 if (name.startsWith("from")) return -370000; 157 158 // create* 159 if (name.equals("create")) return -290000; 160 if (name.equals("create" + type)) return -280000; 161 if (name.startsWith("create")) return -270000; 162 163 // compile* 164 if (name.equals("compile")) return -190000; 165 if (name.equals("compile" + type))return -180000; 166 if (name.startsWith("compile")) return -170000; 167 168 // get* 169 if (name.equals("get")) return 1000; 170 if (name.equals("get" + type)) return 1200; 171 if (name.equals("getInstance")) return 1200; 172 if (name.startsWith("get")) return 1300; 173 174 return 0; 175 } 176}