001package gu.dtalk;
002
003import java.lang.reflect.Type;
004import java.text.ParseException;
005import java.text.SimpleDateFormat;
006import java.util.Date;
007import java.util.HashSet;
008import java.util.Map;
009import java.util.Set;
010import java.util.concurrent.Callable;
011import java.util.concurrent.ExecutionException;
012
013import com.alibaba.fastjson.JSON;
014import com.alibaba.fastjson.JSONException;
015import com.google.common.base.Function;
016import com.google.common.base.Predicate;
017import com.google.common.base.Throwables;
018import com.google.common.cache.Cache;
019import com.google.common.cache.CacheBuilder;
020import com.google.common.collect.Sets;
021
022import gu.simplemq.json.BaseJsonEncoder;
023import net.gdface.utils.FaceUtilits;
024
025import static gu.dtalk.CommonConstant.*;
026import static com.google.common.base.Preconditions.*;
027
028/**
029 * 选项类型
030 * @author guyadong
031 *
032 */
033public enum OptionType {
034        /** 字符串  */
035        STRING(StringOption.class),
036        /** 整数     */
037        INTEGER(IntOption.class),
038        /** 浮点数 */
039        FLOAT(FloatOption.class),
040        /** 布尔型 true/false 0/1 */
041        BOOL(BoolOption.class),
042        /** 日期  yyyy-MM-dd HH:mm:ss  */
043        DATE(DateOption.class),
044        /** url字符串 */
045        URL(UrlOption.class),
046        /** 密码字符串 */
047        PASSWORD(PasswordOption.class),
048        /** e-mail地址 */
049        EMAIL(StringOption.class,"^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"),
050        /** 手机号码(11位) */
051        MPHONE(StringOption.class,"^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$"),
052        /** 身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X */
053        IDNUM(StringOption.class,"(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)"),
054        /** base64 格式二进制数据 */
055        BASE64(Base64Option.class),
056        /** MAC地址二进制数据 */
057        MAC(MACOption.class, "^([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}$"),
058        /** IP地址二进制数据 */
059        IP(IPv4Option.class,"^((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))$"),
060        /** base64 格式JPEG/BMP/PNG格式图像 */
061        IMAGE(ImageOption.class),
062        /** 多选项(n>1) */
063        @SuppressWarnings("unchecked")
064        MULTICHECK(CheckOption.class,"^\\s*(\\d+)?([,;\\s]+\\d+)?\\s*$"),
065        /** 单选开关(n>1) */
066        @SuppressWarnings("unchecked")
067        SWITCH(SwitchOption.class,"^\\s*\\d+\\s*$");
068        final String regex;
069        private volatile Type targetType;
070        /**
071         * 实现数据选项的类
072         */
073        @SuppressWarnings("rawtypes")
074        final Class<? extends BaseOption> optClass;
075        private <T,B extends BaseOption<T>>OptionType(Class<B> implClass) {
076                this(implClass,"");
077        }
078        private <T,B extends BaseOption<T>>OptionType(Class<B> implClass,String regex) {
079                this.optClass = checkNotNull(implClass,"implClass is null");
080                this.regex = checkNotNull(regex,"regex is null");
081        }
082        /**
083         * 字符串验证器,根据正则表达式判断字符串是否符合当前数据类型的格式,
084         * 输入为null或正则表达式不匹配则返回false
085         */
086        public final Predicate<String> strValidator = new Predicate<String>() {
087                @Override
088                public boolean apply(String input) {
089                        if(regex.isEmpty()){
090                                return true;
091                        }
092                        return input != null && input.matches(regex);
093                }
094        };
095        private static final Cache<OptionType, Function<String, ?>> cache = CacheBuilder.newBuilder().build();
096        /**
097         * 返回从字符串转到指定类型的转换器实例<br>
098         * 返回的转换器特征:将字符串转换为指定的类型,如果输入的字符串格式无效则抛出异常
099         * @return
100         */
101        @SuppressWarnings("unchecked")
102        private <T> Function<String, T> internalTrans(){
103
104                switch(this){
105                case EMAIL:
106                case MPHONE:
107                case IDNUM:
108                        // 解析字符串为EMAIL类型,允许输入格式为ff:20:20:20:20:20格式的ip地址
109                        return (Function<String, T>) new Function<String, String>(){
110                                @Override
111                                public String apply(String input) {
112                                        checkArgument(strValidator.apply(input),
113                                                        "INVALID FORMAT '%s' FOR %s", input,OptionType.this.name());
114                                        return input;
115                                }};
116                case DATE:
117                        // 解析字符串为DATE类型,允许日期,时间,日期+时间 三种格式
118                        return (Function<String, T>) new Function<String, Date>(){
119
120                                @Override
121                                public Date apply(String input) {
122                                Date date = null;
123                                if(null != input){
124                                        try {
125                                                date = new SimpleDateFormat(TIMESTAMP_FORMATTER_STR).parse(input);
126                                        } catch (ParseException e) {
127                                                try {
128                                                        date = new SimpleDateFormat(DATE_FORMATTER_STR).parse(input);
129                                                } catch (ParseException e1) {
130                                                        try {
131                                                                date = new SimpleDateFormat(TIME_FORMATTER_STR).parse(input);
132                                                        } catch (ParseException e2) {
133                                                                throw new IllegalArgumentException(String.format("INVALID FORMAT '%s' FOR %s", input,OptionType.this.name()));
134                                                        }
135                                                }
136                                        }
137                                }
138                                        return date;
139                                }};
140                case MAC:
141                        // 解析字符串为MAC类型,允许输入格式为ff:20:20:20:20:20格式的ip地址
142                        return (Function<String, T>) new Function<String, byte[]>(){
143
144                                @Override
145                                public byte[] apply(String input) {
146                                        checkArgument(strValidator.apply(input),
147                                                        "INVALID FORMAT '%s' FOR %s", input,OptionType.this.name());
148                                        String hex = input.replace(":", "");
149                                        return FaceUtilits.hex2Bytes(hex);
150                                }};
151                case IP:
152                        // 解析字符串为ipv4类型,允许输入格式为127.0.0.1格式的ip地址
153                        return (Function<String, T>) new Function<String, byte[]>() {
154                                
155                                @Override
156                                public byte[] apply(String input) {
157                                        checkArgument(strValidator.apply(input),
158                                                        "INVALID FORMAT '%s' FOR %s", input,OptionType.this.name());
159
160                                        String[] ip = input.split("\\.");
161                                        // [192,168,1,1]这样的数组大于127的值直接解析为byte会溢出,所以要先解析为int[]再转为byte[]
162                                        byte[] parseByte = new byte[ip.length];
163                                        for(int i = 0; i < parseByte.length; ++i){
164                                                parseByte[i] = (byte) (Integer.valueOf(ip[i]) & 0xff);
165                                        }
166                                        return parseByte;
167                                
168                                }
169                        };
170                case MULTICHECK:
171                case SWITCH:
172                        // 解析字符串为Set<Integer>类型
173                        return (Function<String, T>) new Function<String, Set<Integer>>(){
174
175                                @Override
176                                public Set<Integer> apply(String input) {
177                                        checkArgument(strValidator.apply(input),
178                                                        "INVALID FORMAT '%s' FOR %s", input,OptionType.this.name());
179
180                                        String[] numlist = input.split("[;,\\s]+");
181                                        HashSet<Integer> set = Sets.newHashSet();
182                                        for(String num:numlist){
183                                                if(!num.isEmpty()){
184                                                        set.add(Integer.valueOf(num));
185                                                }
186                                        }
187                                        return set;
188                                }};             
189                default:
190                        return new DefaultStringTransformer<>(getTargetType());
191                }
192        } 
193        
194        /**
195         * 返回对应类型String到目标数据类型的转换器<br>
196         * 返回的转器实例将字符器转换为当前类型的数据,转换失败则抛出异常
197         * @param <T> 目标数据类型
198         * @return
199         * @see #internalTrans()
200         */
201        @SuppressWarnings("unchecked")
202        public <T> Function<String, T> trans(){
203                try {
204                        return (Function<String, T>) cache.get(this, new Callable<Function<String, T>>(){
205
206                                @Override
207                                public Function<String, T> call() throws Exception {
208                                        return internalTrans();
209                                }});
210                } catch (ExecutionException e) {
211                Throwables.throwIfUnchecked(e.getCause());
212            throw new RuntimeException(e.getCause());
213                }
214        }
215        /**
216         * 如果有定制转换器(非{@link DefaultStringTransformer}),则尝试重新解析name指定的字段
217         * {@value CommonConstant#OPTION_FIELD_VALUE},
218         * {@value CommonConstant#OPTION_FIELD_DEFAULT}
219         * @param json
220         * @param name 字段名
221         */
222        private void refreshValueIfTransPresent(Map<String,Object> json,String name){
223                Function<String, Object> t = trans();
224                // 使用默认transformer的类型直接跳过
225                if(!(t instanceof DefaultStringTransformer)){
226                        if(null != json.get(name)){
227
228                                String valuestr = json.get(name).toString();
229                                try {
230                                        Object parsed = t.apply(valuestr);
231                                        if(parsed != null){
232                                                json.put(name, parsed);
233                                        }       
234                                } catch (Exception e) {
235                                }
236                                
237                        }
238                }
239        }
240        public <T, O extends BaseOption<T>>OptionBuilder<T, O> builder() {
241                return OptionBuilder.<T,O>builder(this);
242        }
243        /**
244         * 默认字符串到T类型的转换器
245         * @author guyadong
246         *
247         * @param <T>
248         */
249        private class DefaultStringTransformer<T> implements Function<String, T>{
250                
251                private final Type type;
252                public DefaultStringTransformer(Type type) {
253                        super();
254                        this.type = checkNotNull(type,"type is null");
255                }
256                /**
257                 * 调用fastjson对输入字符串解析返回指定类型的对象,如果格式不对则抛出异常
258                 * @see com.google.common.base.Function#apply(java.lang.Object)
259                 */
260                @Override
261                public T apply(String input) {
262                        try {
263                                return BaseJsonEncoder.getEncoder().fromJson(input, type);
264                        } catch (JSONException e) {
265                                if( !input.startsWith("\"") && !input.endsWith("\"")){
266                                        try {
267                                                return BaseJsonEncoder.getEncoder().fromJson("\"" + input + "\"", type);
268                                        } catch (JSONException e2) {
269                                        }
270                                }
271                                throw e;
272                        }
273                }               
274        }
275        public static BaseOption<?> parseOption(Map<String,Object> json){
276                OptionType optionType = OptionType.valueOf((String) json.get(OPTION_FIELD_TYPE));
277                optionType.refreshValueIfTransPresent(json, OPTION_FIELD_VALUE);
278                optionType.refreshValueIfTransPresent(json, OPTION_FIELD_DEFAULT);
279                return BaseJsonEncoder.getEncoder().fromJson(   JSON.toJSONString(json), optionType.optClass);
280        }
281        
282        private Type getTargetType() {
283                if(null == targetType){
284                        synchronized (this) {
285                                if (targetType == null) {
286                                        try {
287                                                targetType = optClass.newInstance().type;
288                                        } catch (Exception e) {
289                                                Throwables.throwIfUnchecked(e);
290                                                throw new RuntimeException(e);
291                                        }       
292                                }
293                        }
294                }
295                return targetType;
296        }
297}