content: /**
   * erlyberly, erlang trace debugger
   * Copyright (C) 2016 Andy Till
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program. If not, see <http://www.gnu.org/licenses/>.
   */
  package erlyberly.node;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Stack;
  
  import com.ericsson.otp.erlang.OtpErlangAtom;
  import com.ericsson.otp.erlang.OtpErlangList;
  import com.ericsson.otp.erlang.OtpErlangObject;
  import com.ericsson.otp.erlang.OtpErlangString;
  import com.ericsson.otp.erlang.OtpErlangTuple;
  
  import erlyberly.TraceLog;
  
  public class TraceManager {
  
      private static final OtpErlangAtom RETURN_FROM_ATOM = new OtpErlangAtom("return_from");
  
      private static final OtpErlangAtom EXCEPTION_FROM_ATOM = new OtpErlangAtom("exception_from");
  
      private static final OtpErlangAtom CALL_ATOM = new OtpErlangAtom("call");
  
      private final HashMap<String, Stack<TraceLog>> unfinishedCalls = new HashMap<String, Stack<TraceLog>>();
  
  
      public List<TraceLog> collateTraces(OtpErlangList traceLogs) {
          final ArrayList<TraceLog> traceList = new ArrayList<TraceLog>();
  
          for (OtpErlangObject obj : traceLogs) {
              decodeTraceLog(obj, traceList);
          }
  
          return traceList;
      }
  
      public List<TraceLog> collateTraceSingle(OtpErlangTuple traceLog) {
          final ArrayList<TraceLog> traceList = new ArrayList<TraceLog>();
          decodeTraceLog(traceLog, traceList);
          return traceList;
      }
  
      private void decodeTraceLog(OtpErlangObject otpErlangObject, ArrayList<TraceLog> traceList) {
          OtpErlangTuple tup = (OtpErlangTuple) otpErlangObject;
          OtpErlangAtom traceType = (OtpErlangAtom) tup.elementAt(0);
  
          if(CALL_ATOM.equals(traceType)) {
              TraceLog trace = proplistToTraceLog(tup);
  
              Stack<TraceLog> stack = unfinishedCalls.get(trace.getPidString());
  
              if(stack == null)
                  stack = new Stack<TraceLog>();
  
              stack.add(trace);
  
              unfinishedCalls.put(trace.getPidString(), stack);
  
              traceList.add(trace);
          }
          else if(RETURN_FROM_ATOM.equals(traceType) || EXCEPTION_FROM_ATOM.equals(traceType)) {
              Map<Object, Object> map = propsFromTrace(tup);
  
              Object object = map.get(TraceLog.ATOM_PID);
  
              if(object != null) {
                  OtpErlangString pidString = (OtpErlangString) object;
  
                  Stack<TraceLog> stack = unfinishedCalls.get(pidString.stringValue());
                  if(stack == null)
                      return;
  
                  TraceLog traceLog = stack.pop();
                  traceLog.complete(map);
  
                  if(stack.isEmpty())
                      unfinishedCalls.remove(pidString.stringValue());
              }
  
          }
      }
  
      private TraceLog proplistToTraceLog(OtpErlangTuple tup) {
          Map<Object, Object> map = propsFromTrace(tup);
  
          TraceLog trace = new TraceLog(map);
  
          return trace;
      }
  
      private Map<Object, Object> propsFromTrace(OtpErlangTuple tup) {
          return OtpUtil.propsToMap((OtpErlangList) tup.elementAt(1));
      }
  }
  