content: /*
   * 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 org.apache.weex.utils;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  import java.util.concurrent.CopyOnWriteArraySet;
  
  import android.support.annotation.Nullable;
  import android.text.TextUtils;
  import org.apache.weex.WXEnvironment;
  import org.apache.weex.WXSDKInstance;
  import org.apache.weex.WXSDKManager;
  import org.apache.weex.adapter.IWXConfigAdapter;
  import org.apache.weex.adapter.IWXJSExceptionAdapter;
  import org.apache.weex.common.WXErrorCode;
  import org.apache.weex.common.WXErrorCode.ErrorGroup;
  import org.apache.weex.common.WXJSExceptionInfo;
  import org.apache.weex.common.WXPerformance;
  import org.apache.weex.performance.WXAnalyzerDataTransfer;
  import org.apache.weex.performance.WXInstanceApm;
  import org.apache.weex.performance.WXStateRecord;
  
  /**
   * Created on 2017/10/13.
   */
  
  public class WXExceptionUtils {
  
      private static Set<String> sGlobalExceptionRecord = new CopyOnWriteArraySet<String>();
  
  	/**
  	 * degradeUrl for degrade case
  	 */
  	public static String degradeUrl = "BundleUrlDefaultDegradeUrl";
  
  	private static boolean checkNeedReportCauseRepeat(String instanceId, WXErrorCode errCode,String exception){
  
  	    if (TextUtils.isEmpty(exception)){
  	        return true;
          }
  
          if (null != errCode && errCode.getErrorGroup() != ErrorGroup.JS){
  	        return true;
          }
  
          if (TextUtils.isEmpty(instanceId)){
              instanceId = "instanceIdNull";
          }
  
          String targetException = exception.length() > 200 ?exception.substring(0,200) : exception;
          Set<String> recordExceptionHistory= null;
  
          WXSDKInstance instance =  WXSDKManager.getInstance().getAllInstanceMap().get(instanceId);
          if (null == instance){
              recordExceptionHistory = sGlobalExceptionRecord;
          }else {
              recordExceptionHistory =  instance.getApmForInstance().exceptionRecord;
          }
  
          if (null == recordExceptionHistory){
              return true;
          }
  
          if (recordExceptionHistory.contains(targetException)){
              return false;
          }
          recordExceptionHistory.add(targetException);
          return true;
      }
  
  
  
  
  	/**
  	 * commitCriticalExceptionRT eg:JsRuntime Exception or JsFramework Init Exception
  	 * @param instanceId
  	 * @param errCode
  	 * @param function
  	 * @param exception
  	 * @param extParams
  	 */
  	public static void commitCriticalExceptionRT(@Nullable final String instanceId,
  												 @Nullable final WXErrorCode errCode,
  												 @Nullable final String function,
  												 @Nullable final String exception,
  												 @Nullable final Map<String,String> extParams ) {
  
          try {
              WXLogUtils.e("weex","commitCriticalExceptionRT :"+errCode+"exception"+exception);
              WXStateRecord.getInstance().recordException(instanceId,exception);
              IWXConfigAdapter configAdapter = WXSDKManager.getInstance().getWxConfigAdapter();
              boolean doCheck = true;
              if (null != configAdapter){
                  String value = configAdapter.getConfig("wxapm","check_repeat_report","true");
                  doCheck = "true".equalsIgnoreCase(value);
              }
              boolean doReport =true;
              if (doCheck){
                  doReport = checkNeedReportCauseRepeat(instanceId,errCode,exception);
              }
              if (!doReport){
                  return;
              }
          }catch (Throwable e){
              e.printStackTrace();
          }
  
  
  		commitCriticalExceptionWithDefaultUrl(
  		    "BundleUrlDefault",
              instanceId,
              errCode,
              function,
              exception,
              extParams
          );
  	}
  
      public static void commitCriticalExceptionWithDefaultUrl(
          @Nullable final String defaultUrl,
          @Nullable final String instanceId,
          @Nullable final WXErrorCode errCode,
          @Nullable final String function,
          @Nullable final String exception,
          @Nullable final Map<String,String> extParams
      ){
          IWXJSExceptionAdapter adapter = WXSDKManager.getInstance().getIWXJSExceptionAdapter();
          WXSDKInstance instance = null;
          WXJSExceptionInfo exceptionCommit;
          String bundleUrlCommit = TextUtils.isEmpty(defaultUrl)?"BundleUrlDefault":defaultUrl;
          String instanceIdCommit = "InstanceIdDefalut";
          String exceptionMsgCommit = exception;
          Map<String, String> commitMap = extParams;
          if (null == commitMap){
              commitMap = new HashMap<>();
          }
          commitMap.put("wxSdkInitStartTime", String.valueOf(WXEnvironment.sSDKInitStart));
          commitMap.put("wxSDKInitCostTime", String.valueOf(WXEnvironment.sSDKInitTime));
          commitMap.put("wxSDKCurExceptionTime", String.valueOf(System.currentTimeMillis()));
          commitMap.put("wxUseRuntimeApi",String.valueOf(WXEnvironment.sUseRunTimeApi));
          if (!TextUtils.isEmpty(instanceId)) {
              instanceIdCommit = instanceId;
              instance = WXSDKManager.getInstance().getAllInstanceMap().get(instanceId);
  
              if (null != instance) {
                  bundleUrlCommit = instance.getApmForInstance().reportPageName;
                  Object loadLength = instance.getApmForInstance().extInfo.get(WXInstanceApm.VALUE_BUNDLE_LOAD_LENGTH);
                  String loadLengthStr = (loadLength instanceof Integer)?String.valueOf(loadLength):"unknownLength";
                  commitMap.put(WXInstanceApm.VALUE_BUNDLE_LOAD_LENGTH,loadLengthStr);
                  commitMap.put("templateInfo",instance.getTemplateInfo());
                  if (TextUtils.isEmpty(bundleUrlCommit) || bundleUrlCommit.equals(WXPerformance.DEFAULT)) {
                      if (!TextUtils.equals(degradeUrl, "BundleUrlDefaultDegradeUrl")) {
                          bundleUrlCommit = degradeUrl;
                      } else
                          bundleUrlCommit = WXSDKInstance.requestUrl;
                  }
                  for (Map.Entry<String,String> entry: instance.getContainerInfo().entrySet()){
                      commitMap.put(entry.getKey(),entry.getValue());
                  }
                  commitMap.put("wxStageList",convertStageToStr(instance));
                  String bundleTemplate = instance.getTemplate();
                  if (null == bundleTemplate){
                      bundleTemplate = "has recycle by gc";
                  }else {
                      int length = bundleTemplate.length();
                      bundleTemplate = bundleTemplate.substring(0,Math.min(length,300));
                  }
                  commitMap.put("wxTemplateOfBundle",bundleTemplate);
  
                  Long pageStartTime = instance.getApmForInstance().stageMap.get(WXInstanceApm.KEY_PAGE_STAGES_DOWN_BUNDLE_START);
                  if (null == pageStartTime){
                      pageStartTime = instance.getApmForInstance().stageMap.get(WXInstanceApm.KEY_PAGE_STAGES_RENDER_ORGIGIN);
                  }
                  if (null != pageStartTime){
                      commitMap.put("wxUseTime", String.valueOf(WXUtils.getFixUnixTime() - pageStartTime));
                  }
              }
          } else {//instance is null for instance id is null
              if (commitMap.size() > 0) {
                  bundleUrlCommit = TextUtils.isEmpty(commitMap.get("weexUrl")) ? commitMap.get("weexUrl")
                      : commitMap.get("bundleUrl");
              }
          }
  
          String illegalValue = commitMap.get("errorCode");
          if (null != illegalValue && illegalValue.length()>200){
              commitMap.remove("errorCode");
          }
  
          exceptionCommit = new WXJSExceptionInfo(instanceIdCommit, bundleUrlCommit, errCode, function, exceptionMsgCommit, commitMap);
          if (adapter != null) {
              adapter.onJSException(exceptionCommit);
          }
  
          WXAnalyzerDataTransfer.transferError(exceptionCommit, instanceId);
      }
  
      private static String convertStageToStr(WXSDKInstance instance) {
          if (null == instance || null == instance.getApmForInstance() || instance.getApmForInstance().stageMap.isEmpty()) {
              return "noStageRecord";
          }
          List<Entry<String, Long>> list = new ArrayList<>(instance.getApmForInstance().stageMap.entrySet());
          Collections.sort(list, new Comparator<Entry<String, Long>>() {
              @Override
              public int compare(Entry<String, Long> o1, Entry<String, Long> o2) {
                  return (int)(o1.getValue() - o2.getValue());
              }
          });
  
          StringBuilder builder = new StringBuilder();
          for (Map.Entry<String, Long> entry : list) {
              builder.append(entry.getKey()).append(':').append(entry.getValue()).append("->");
          }
          return builder.toString();
      }
  
  }