content: /*
   *  Copyright Beijing 58 Information Technology Co.,Ltd.
   *
   *  Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you under the Apache License, Version 2.0 (the
   *  "License"); you may not use this file except in compliance
   *  with the License.  You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing,
   *  software distributed under the License is distributed on an
   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   *  KIND, either express or implied.  See the License for the
   *  specific language governing permissions and limitations
   *  under the License.
   */
  package com.bj58.spat.gaea.server.core.communication.http;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import com.bj58.sfft.json.JsonHelper;
  import com.bj58.spat.gaea.server.contract.annotation.HttpParameterType;
  import com.bj58.spat.gaea.server.contract.annotation.HttpRequestMethod;
  import com.bj58.spat.gaea.server.contract.context.Global;
  import com.bj58.spat.gaea.server.contract.log.ILog;
  import com.bj58.spat.gaea.server.contract.log.LogFactory;
  import com.bj58.spat.gaea.server.core.communication.http.Action.Parameter;
  import com.bj58.spat.gaea.server.core.convert.IConvert;
  import com.bj58.spat.gaea.server.core.convert.JavaConvert;
  import com.bj58.spat.gaea.server.core.convert.JsonConvert;
  import com.bj58.spat.gaea.server.deploy.bytecode.ClassInfo;
  import com.bj58.spat.gaea.server.deploy.bytecode.ScanClass;
  import com.bj58.spat.gaea.server.deploy.bytecode.ClassInfo.MethodInfo;
  import com.bj58.spat.gaea.server.deploy.bytecode.ClassInfo.ParamInfo;
  import com.bj58.spat.gaea.server.deploy.hotdeploy.DynamicClassLoader;
  
  /**
   * ServerStateType
   * 
   * @author Service Platform Architecture Team (spat@58.com)
   */
  public class RequestMapping {
  	
  	/**
  	 * logger
  	 */
  	private static ILog logger = LogFactory.getLogger(RequestMapping.class);
  	
  	private static Map<String, Controler> urlMap = new HashMap<String, Controler>();
  	
  	private static IConvert javaConvert = new JavaConvert();
  	
  	private static IConvert jsonConvert = new JsonConvert();
  	
  	private static Pattern patternParam = Pattern.compile("\\{(.+?)\\}");
  
  	/**
  	 * get action
  	 * @param context
  	 * @return
  	 * @throws Exception
  	 */
  	public static Action getAction(HttpContext context) throws Exception {
  		logger.debug("http-method:" + context.getMethod());
  		logger.debug("uri:" + context.getUri());
  		String regUrl = getKey(context.getUri());
  		logger.debug("reg-url:" + regUrl);
  		
  		if(regUrl == null || regUrl.equalsIgnoreCase("")) {
  			throw new HttpException("url not match!!!", 404, null);
  		}
  		
  		Controler controler = urlMap.get(regUrl);
  		
  		Action action = getAction(context.getMethod(), controler);
  		
  
  		//set path param
  		Pattern pattern = Pattern.compile(regUrl);
  		Matcher matcher = pattern.matcher(context.getUri());
  		if(matcher.find()) {
  			for(Parameter para : action.getParamList()) {
  				if(para.getParaType() == HttpParameterType.PathParameter) {
  					logger.debug("url para-mapping:" 
  							+ para.getMapping() 
  							+ "--value:" 
  							+ matcher.group(para.getUrlParaIndex() + 1));
  					String value = matcher.group(para.getUrlParaIndex() + 1);
  					para.setValue(value);
  				}
  			}
  		}
  		
  		
  		//set content param
  		if(context.getContentBuffer() != null && context.getContentBuffer().length > 0) {
  			String content = new String(context.getContentBuffer(),
  					Global.getSingleton().getServiceConfig().getString("gaea.encoding"));
  			KeyValue[] kvs = (KeyValue[]) JsonHelper.toJava(content, KeyValue[].class);
  			for(KeyValue kv : kvs) {
  				for(Parameter para : action.getParamList()) {
  					if(para.getMapping().equalsIgnoreCase(kv.getKey())) {
  						logger.debug("content para-mapping:" 
  								+ para.getMapping() 
  								+ "--value:" 
  								+ kv.getValue());
  						
  //						Object value = convertValue(para.getType(), kv.getValue());
  						String value = kv.getValue();
  						para.setValue(value);
  						break;
  					}
  				}
  			}
  		}
  		
  		return action;
  	}
  	
  	
  	/**
  	 * get cache key
  	 * @param uri
  	 * @return
  	 */
  	protected static String getKey(String uri) {
  		Set<String> set = urlMap.keySet();
  		for(String reg : set) {
  			if(uri.matches(reg)) {
  				return reg;
  			}
  		}
  		return null;
  	}
  	
  	/**
  	 * get url param index
  	 * @param uri
  	 * @param mapping
  	 * @return
  	 * @throws Exception
  	 */
  	protected static int getUrlParaIndex(String uri, String mapping) throws Exception {
  		Matcher matcher = patternParam.matcher(uri);
  		
  		int index = 0;
  		while(matcher.find()) {
  			if(matcher.group(1).equalsIgnoreCase(mapping)) {
  				return index;
  			}
  			index++;
  		}
  		throw new Exception("not mapping");
  	}
  	
  	
  	/**
  	 * get content para index
  	 * @param piAry
  	 * @param para
  	 * @return
  	 */
  	protected static int getContentParaIndex(ParamInfo[] piAry, ParamInfo para) {
  		int index = 0;
  		for(ParamInfo pi : piAry) {
  			if(pi == para) {
  				return index;
  			} else if(pi.getHttpPathParameter() != null 
  					 && pi.getHttpPathParameter().paramType() == HttpParameterType.ContentParameter) {
  				index++;
  			}
  		}
  		return index;
  	}
  	
  	/**
  	 * get action from controler
  	 * @param method
  	 * @param controler
  	 * @return
  	 */
  	protected static Action getAction(HttpRequestMethod method, Controler controler) {
  		Action action = null;
  		switch(method) {
  			case GET:
  				action = controler.getGetAction();
  				break;
  			case POST:
  				action = controler.getPostAction();
  				break;
  			case DELETE:
  				action = controler.getDeleteAction();
  				break;
  			case PUT:
  				action = controler.getPutAction();
  				break;
  			case HEAD:
  				action = controler.getHeadAction();
  				break;
  			default:
  				action = controler.getGetAction();
  				break;
  		}
  		
  		return action;
  	}
  	
  	/**
  	 * create url regex
  	 * @param sourceUrl
  	 * @param piAry
  	 * @return
  	 */
  	protected static String createRegexUrl(String sourceUrl, ParamInfo[] piAry){
  		String regUrl = sourceUrl.toLowerCase();
  		for(ParamInfo pi : piAry) {
  			if(pi.getHttpPathParameter() == null
  			   || pi.getHttpPathParameter().paramType() == HttpParameterType.PathParameter) {
  				
  				if(pi.getCls() == byte.class || pi.getCls() == Byte.class
  						|| pi.getCls() == short.class || pi.getCls() == Short.class
  						|| pi.getCls() == int.class || pi.getCls() == Integer.class
  						|| pi.getCls() == long.class || pi.getCls() == Long.class
  						|| pi.getCls() == double.class || pi.getCls() == Double.class){
  					regUrl = regUrl.replaceFirst("\\{"+ pi.getMapping().toLowerCase() +"\\}", "(\\\\d+)");
  				} else {
  					regUrl = regUrl.replaceFirst("\\{"+ pi.getMapping().toLowerCase() +"\\}", "(\\\\w+)");
  				}
  			}
  		}
  		return regUrl;
  	}
  	
  	/**
  	 * convert string to target value
  	 * @param type
  	 * @param value
  	 * @return
  	 * @throws Exception
  	 */
  	protected static Object convertValue(String type, String value) throws Exception {
  		Object obj = null;
  		if (type.equalsIgnoreCase("String")) {
  			obj = value;
  		} else if (type.equalsIgnoreCase("int") || type.equalsIgnoreCase("Integer")) {
  			obj = javaConvert.convertToint(value);
  		} else if (type.equalsIgnoreCase("long")) {
  			obj = javaConvert.convertTolong(value);
  		} else if (type.equalsIgnoreCase("short")) {
  			obj = javaConvert.convertToshort(value);
  		} else if (type.equalsIgnoreCase("float")) {
  			obj = javaConvert.convertTofloat(value);
  		} else if (type.equalsIgnoreCase("boolean")) {
  			obj = javaConvert.convertToboolean(value);
  		} else if (type.equalsIgnoreCase("double")) {
  			obj = javaConvert.convertTodouble(value);
  		} else if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("Character")) {
  			obj = javaConvert.convertTochar(value);
  		} else if (type.equalsIgnoreCase("byte")) {
  			obj = javaConvert.convertTobyte(value);
  		} else {
  			obj = jsonConvert.convertToT(value, Class.forName(type));
  		}
  		
  		return obj;
  	}
  	
  	/**
  	 * init url mapping
  	 * @throws Exception
  	 */
  	public static void init() throws Exception {
  		logger.info("begin init http request mapping...");
  		
  		urlMap.clear();
  		
  		StringBuffer sbMsg = new StringBuffer();
  		
  		String servicePath = Global.getSingleton().getRootPath() 
  							 + "/service/deploy/" 
  							 + Global.getSingleton().getServiceConfig().getString("gaea.service.name");
  		
  		DynamicClassLoader classLoader = new DynamicClassLoader();
  		classLoader.addFolder(servicePath);
  		
  		List<ClassInfo> ciList = ScanClass.getBehaviorClassInfos(servicePath, classLoader);
  		for(ClassInfo ci : ciList) {
  			List<MethodInfo> miList = ci.getMethodList();
  			for(MethodInfo mi : miList) {
  				if(mi.getHttpRequestMapping() != null) {
  					
  					String uri = mi.getHttpRequestMapping().uri();
  					String regUrl = createRegexUrl(uri, mi.getParamInfoAry());
  					Controler controler = urlMap.get(regUrl);
  					if(controler == null) {
  						controler = new Controler();
  					}
  					
  					sbMsg.append("\n----------------- url mapping -----------------");
  					sbMsg.append("\nurl:" + uri);
  					sbMsg.append("\nreg-url:" + regUrl);
  					sbMsg.append("\nlookUP:");
  					sbMsg.append(ci.getLookUP());
  					sbMsg.append("\nmethodName:");
  					sbMsg.append(mi.getMethod().getName());
  					
  					Action action = new Action();
  					action.setLookup(ci.getLookUP());
  					action.setMethodName(mi.getMethod().getName());
  
  					List<Parameter> paramList = new ArrayList<Parameter>();
  					ParamInfo[] piAry = mi.getParamInfoAry();
  					for(int i=0; i<piAry.length; i++) {
  						
  						String paraTypeName = piAry[i].getType().toString().replaceFirst("class ", "");
  						if(paraTypeName.startsWith("[")){
  							paraTypeName = piAry[i].getCls().getCanonicalName();
  						}
  						
  						sbMsg.append("\n\nparaTypeName:");
  						sbMsg.append(paraTypeName);
  						sbMsg.append("\nmapping:");
  						sbMsg.append(piAry[i].getMapping());
  						
  						if(piAry[i].getHttpPathParameter() != null
  						   && piAry[i].getHttpPathParameter().paramType() == HttpParameterType.ContentParameter) {
  							
  							paramList.add(new Parameter(paraTypeName, 
  														piAry[i].getMapping(), 
  														null, 
  														0, 
  														getContentParaIndex(piAry, piAry[i]), 
  														piAry[i].getIndex(), 
  														HttpParameterType.ContentParameter)
  												 );
  							
  							sbMsg.append("\ncontentParamIndex:");
  							sbMsg.append(getContentParaIndex(piAry, piAry[i]));
  							sbMsg.append("\nparamType:");
  							sbMsg.append(HttpParameterType.ContentParameter);
  							
  						} else {
  							paramList.add(new Parameter(paraTypeName, 
  														piAry[i].getMapping(), 
  														null, 
  														getUrlParaIndex(uri, piAry[i].getMapping()), 
  														0, 
  														piAry[i].getIndex(), 
  														HttpParameterType.PathParameter)
  												 );
  							
  							sbMsg.append("\nurlParamIndex:");
  							sbMsg.append(getUrlParaIndex(uri, piAry[i].getMapping()));
  							sbMsg.append("\nparamType:");
  							sbMsg.append(HttpParameterType.PathParameter);
  						}
  						
  						sbMsg.append("\nmethodParamIndex:");
  						sbMsg.append(piAry[i].getIndex());
  					}
  
  					action.setParamList(paramList);
  					
  					HttpRequestMethod[] methodAry = mi.getHttpRequestMapping().method();
  					for(HttpRequestMethod method : methodAry) {
  						sbMsg.append("\nhttpMethod:");
  						sbMsg.append(method);
  						
  						if(method == HttpRequestMethod.GET) {
  							controler.setGetAction(action);
  						} else if(method == HttpRequestMethod.POST) {
  							controler.setPostAction(action);
  						} else if(method == HttpRequestMethod.DELETE) {
  							controler.setDeleteAction(action);
  						} else if(method == HttpRequestMethod.HEAD) {
  							controler.setHeadAction(action);
  						} else if(method == HttpRequestMethod.PUT) {
  							controler.setPutAction(action);
  						}
  					}
  
  					urlMap.put(regUrl, controler);
  					
  					sbMsg.append("\n\n");
  				}
  			}
  		}
  		logger.debug(sbMsg.toString());
  		
  		logger.info("finish init http request mapping!");
  	}
  	
  	
  }