001package gu.dtalk;
002
003import java.lang.reflect.Type;
004import java.util.Arrays;
005import java.util.Collection;
006import java.util.Collections;
007import java.util.List;
008import java.util.Map;
009
010import com.alibaba.fastjson.annotation.JSONField;
011import com.alibaba.fastjson.parser.ParserConfig;
012import com.alibaba.fastjson.util.TypeUtils;
013import com.google.common.base.Function;
014import com.google.common.base.MoreObjects;
015import com.google.common.base.Throwables;
016import com.google.common.collect.Collections2;
017import com.google.common.collect.Lists;
018import com.google.common.collect.Maps;
019
020import gu.dtalk.exception.CmdExecutionException;
021import static com.google.common.base.Preconditions.*;
022
023/**
024 * 设备命令条目
025 * @author guyadong
026 *
027 */
028public class CmdItem extends BaseItem {
029
030        private static final Function<BaseItem, BaseOption<Object>> TO_OPTION = new Function<BaseItem,BaseOption<Object>>(){
031
032                @SuppressWarnings("unchecked")
033                @Override
034                public BaseOption<Object> apply(BaseItem input) {
035                        return (BaseOption<Object>) input;
036                }};
037        private static final Function<BaseOption<?>,BaseItem> TO_ITEM = new Function<BaseOption<?>,BaseItem>(){
038
039                @Override
040                public BaseItem apply(BaseOption<?> input) {
041                        return input;
042                }};
043        private static final Function<BaseItem, Object> TO_VALUE = new Function<BaseItem, Object>() {
044                @Override
045                public Object apply(BaseItem input) {
046                        return ((BaseOption<?>) input).fetch();
047                }
048        };
049        @JSONField(serialize = false,deserialize = false)
050        private ICmdAdapter cmdAdapter;
051        
052        /**
053         * 任务队列名<br>
054         * 该字段不为空时,对象支持队列任务
055         */
056        private String taskQueue;
057        public CmdItem() {
058        }
059
060        @Override
061        public final boolean isContainer() {
062                return true;
063        }
064
065        @Override
066        public final ItemType getCatalog() {
067                return ItemType.CMD;
068        }
069        @JSONField(serialize = false,deserialize = false)
070        public List<BaseOption<Object>> getParameters(){
071                return Lists.transform(getChilds(),TO_OPTION);
072        }
073        @JSONField(serialize = false,deserialize = false)
074        public void setParameters(List<BaseOption<?>> parameters){
075                items.clear();
076                addParameters(parameters);
077        }
078        public CmdItem addParameters(BaseOption<?> ... parameter){
079                return addParameters(Arrays.asList(parameter));
080        }
081        public CmdItem addParameters(Collection<BaseOption<?>> parameters){
082                addChilds(Collections2.transform(parameters, TO_ITEM));
083                return this;
084        }
085        public ICmdAdapter getCmdAdapter() {
086                return cmdAdapter;
087        }
088        public CmdItem setCmdAdapter(ICmdAdapter cmdAdapter) {
089                this.cmdAdapter = cmdAdapter;
090                return this;
091        }
092    /**
093     * 将{@code value}转为{@code type}指定的类型
094     * @param <T> 目标参数类型
095     * @param value
096     * @param type
097     * @return
098     * @see TypeUtils#cast(Object, Type, ParserConfig)
099     */
100    @SuppressWarnings("unchecked")
101    public static final <T> T cast(Object value,Type type){
102        return (T)TypeUtils.cast(value,type,ParserConfig.getGlobalInstance());
103    }
104        /**
105         * 更新命令参数
106         * @param parameters
107         * @return
108         */
109        private CmdItem updateParameter(Map<String, ?> parameters){
110                parameters = MoreObjects.firstNonNull(parameters, Collections.<String, Object>emptyMap());
111                for(BaseOption<Object> param : getParameters()){
112                        Object value = cast(parameters.get(param.getName()), param.javaType());
113                        param.updateFrom(value);
114                }       
115                return this;
116        }
117        /**
118         * 参数 检查,如果参数为required,而输入参数中不包含这个参数,则抛出异常
119         * @param input
120         * @return input
121         */
122        private Map<String, Object> checkRequired(Map<String, Object> input){
123                for(BaseOption<Object> param : getParameters()){
124                        checkArgument(!param.isRequired() || input.containsKey(param.getName()),
125                                        "MISS REQUIRED PARAM %s",param.getName());
126                }
127                return input;
128        }
129        /**
130         * 执行命令
131         * @return
132         * @throws CmdExecutionException 设备命令执行异常
133         */
134        public final Object runCmd() throws CmdExecutionException{
135                synchronized (items) {
136                        if(cmdAdapter !=null){
137                                try {
138                                        // 将 parameter 转为 Map<String, Object>
139                                        Map<String, Object> objParams = Maps.transformValues(items, TO_VALUE);
140                                        return cmdAdapter.apply(checkRequired(objParams));                                      
141                                } finally {
142                                        reset();
143                                }
144                        }
145                        return null;
146                }
147        }
148        /**
149         * 执行命令
150         * @param parameters 命令参数
151         * @return
152         * @throws CmdExecutionException 设备命令执行异常
153         */
154        final Object runCmd(Map<String, ?> parameters) throws CmdExecutionException{
155                synchronized (items) {                  
156                        if(cmdAdapter !=null){
157                                updateParameter(parameters);
158                                try {
159                                        // 将 parameter 转为 Map<String, Object>
160                                        Map<String, Object> objParams = Maps.transformValues(items, TO_VALUE);
161                                        return cmdAdapter.apply(checkRequired(objParams));
162                                } finally {
163                                        reset();
164                                }
165                        }
166                        return null;
167                }
168        }
169        @SuppressWarnings("unchecked")
170        public <T>BaseOption<T> getParameter(final String name){
171                return (BaseOption<T>) getChild(name);
172        }
173        
174        /**
175         * 设置所有参数为{@code null}
176         * @return 返回当前对象
177         */
178        public CmdItem reset(){
179                for (BaseOption<Object> item : getParameters()) {
180                        item.setValue(null);
181                }
182                return this;
183        }
184        
185        /**
186         * 将当前命令作为任务对象注册到指定的任务队列,可以执行队列中的任务<br>
187         * {@link #cmdAdapter}为{@code null}时无效
188         * @param queue
189         * @return 返回当前对象
190         */
191        public CmdItem asTaskAdapter(String queue){
192                if(cmdAdapter != null){
193                        new TaskAdapter(queue)
194                                .setCmdAdapter(cmdAdapter)
195                                .register();
196                        this.taskQueue = queue;
197                }
198                return this;
199        }
200        /**
201         * 将当前命令作为任务对象注册到指定的任务队列,可以执行队列中的任务<br>
202         * {@link #cmdAdapter}为{@code null}时无效
203         * @param taskAdatperClass 任务对象类,必须有(String)构造方法,应用层可以继承{@link TaskAdapter}
204         *      重写{@link TaskAdapter#makeAck(Object, Exception, String, Long)}方法,返回不同的响应对象
205         * @return 返回当前对象
206         */
207        public CmdItem asTaskAdapter(String queue,Class<? extends TaskAdapter> taskAdatperClass){
208                if(cmdAdapter != null){
209                        try {
210                                checkNotNull(taskAdatperClass,"taskAdatperClass is null")
211                                        .getConstructor(String.class)
212                                        .newInstance(queue)
213                                        .setCmdAdapter(cmdAdapter)
214                                        .register();
215                                this.taskQueue = queue;
216                        } catch (Exception e) {
217                                Throwables.throwIfUnchecked(e);
218                                throw new RuntimeException(e);
219                        }
220                }
221                return this;
222        }
223        /**
224         * 设备命令执行接口
225         * @author guyadong
226         *
227         */
228        public static interface ICmdAdapter {
229                /**
230                 * 执行设备命令
231                 * @param input 以值对(key-value)形式提供的输入参数
232                 * @return 命令返回值,没有返回值则返回{@code null}
233                 * @throws CmdExecutionException 命令执行失败
234                 */
235                Object apply(Map<String, Object> input) throws CmdExecutionException;
236        }
237        /**
238         * @return taskQueue
239         */
240        public String getTaskQueue() {
241                return taskQueue;
242        }
243
244        /**
245         * @param taskQueue 要设置的 taskQueue
246         */
247        public void setTaskQueue(String taskQueue) {
248                this.taskQueue = taskQueue;
249        }
250}