public class ExceptionViewer {
  private static final ArrayDeque<Object[]> array = new ArrayDeque<Object[]>(32);
  private static final Calendar calendar = Calendar.getInstance();
  private static boolean isPopup = false;

  public static final void notifyPopup(final boolean p) {
    CCoreUtil.checkAccess();

    isPopup = p;
  }

  public static final Vector<String> exception = new Vector<String>();
  public static final Vector<StackTraceElement[]> stacks = new Vector<StackTraceElement[]>();
  private static ExceptionViewer msbViewer;

  private JFrame dialog;
  private final JButton clearBtn =
      new JButton((String) ResourceUtil.get(8005), new ImageIcon(ImageSrc.REMOVE_SMALL_ICON));
  private int currRow;
  private final JTable tableException, tableStacks;
  private final AbstractTableModel modelException, modelStacks;

  final Runnable refreshTable =
      new Runnable() {
        @Override
        public void run() {
          tableException.updateUI();
        }
      };

  private final void reset() {
    synchronized (exception) {
      exception.clear();
      stacks.clear();
      currRow = 0;

      tableException.updateUI();
      tableStacks.updateUI();
    }
  }

  public ExceptionViewer() {

    clearBtn.addActionListener(
        new HCActionListener() {
          @Override
          public void actionPerformed(final ActionEvent e) {
            reset();
          }
        });

    modelException =
        new AbstractTableModel() {
          @Override
          public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
            return;
          }

          @Override
          public void removeTableModelListener(final TableModelListener l) {}

          @Override
          public boolean isCellEditable(final int rowIndex, final int columnIndex) {
            return false;
          }

          @Override
          public Object getValueAt(final int rowIndex, final int columnIndex) {
            final String rowValue = exception.elementAt(rowIndex);
            return rowValue == null ? "" : rowValue;
          }

          @Override
          public int getRowCount() {
            return exception.size();
          }

          @Override
          public String getColumnName(final int columnIndex) {
            return "Exception Descrition";
          }

          @Override
          public int getColumnCount() {
            return 1;
          }

          @Override
          public Class<?> getColumnClass(final int columnIndex) {
            return String.class;
          }

          @Override
          public void addTableModelListener(final TableModelListener l) {}
        };

    modelStacks =
        new AbstractTableModel() {
          @Override
          public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
            return;
          }

          @Override
          public void removeTableModelListener(final TableModelListener l) {}

          @Override
          public boolean isCellEditable(final int rowIndex, final int columnIndex) {
            return false;
          }

          @Override
          public Object getValueAt(final int rowIndex, final int columnIndex) {
            if (rowIndex >= getRowCount()) {
              return "";
            }
            final StackTraceElement[] value = stacks.elementAt(currRow);
            if (value == null) {
              return "";
            } else {
              if (rowIndex < value.length) {
                return value[rowIndex];
              } else {
                return "";
              }
            }
          }

          @Override
          public int getRowCount() {
            if (currRow < stacks.size()) {
              return stacks.elementAt(currRow).length;
            }
            return 0;
          }

          @Override
          public String getColumnName(final int columnIndex) {
            return "StackTraceElement";
          }

          @Override
          public int getColumnCount() {
            return 1;
          }

          @Override
          public Class<?> getColumnClass(final int columnIndex) {
            return String.class;
          }

          @Override
          public void addTableModelListener(final TableModelListener l) {}
        };

    tableException = new JTable(modelException);
    tableStacks = new JTable(modelStacks);

    //		{
    //			HCHeaderRenderer rend = new
    // HCHeaderRenderer(tableException.getTableHeader().getDefaultRenderer());
    //			tableException.getTableHeader().setDefaultRenderer(rend);
    //		}
    //		{
    //			HCHeaderRenderer rend = new
    // HCHeaderRenderer(tableStacks.getTableHeader().getDefaultRenderer());
    //			tableStacks.getTableHeader().setDefaultRenderer(rend);
    //		}

    tableException
        .getSelectionModel()
        .addListSelectionListener(
            new ListSelectionListener() {
              @Override
              public void valueChanged(final ListSelectionEvent e) {
                if (e.getValueIsAdjusting() == false) {
                  final ListSelectionModel lsm = (ListSelectionModel) e.getSource();
                  if (lsm.isSelectionEmpty()) {
                    return;
                  } else {
                    final int minIndex = lsm.getMinSelectionIndex();
                    final int maxIndex = lsm.getMaxSelectionIndex();
                    for (int i = minIndex; i <= maxIndex; i++) {
                      if (lsm.isSelectedIndex(i)) {
                        currRow = i;
                        tableStacks.updateUI();
                        return;
                      }
                    }
                  }
                }
              }
            });
  }

  private final void show() {
    if (exception.size() == 0) {
      return;
    }

    if (dialog != null) {
      updateUI();
      return;
    }

    final JPanel panel = new JPanel(new GridBagLayout());
    final Insets insets = new Insets(ClientDesc.hgap, ClientDesc.hgap, 0, ClientDesc.vgap);
    {
      final GridBagConstraints c = new GridBagConstraints();
      c.insets = insets;
      c.gridy = 0;
      c.anchor = GridBagConstraints.CENTER;
      c.fill = GridBagConstraints.BOTH;
      c.weighty = 0.7;
      c.weightx = 1.0;

      tableException.setRowSelectionAllowed(true);
      //			panel.add(tableException, c);
      final JScrollPane scrollPane = new JScrollPane(tableException);
      scrollPane.setPreferredSize(new Dimension(500, 200));
      panel.add(scrollPane, c);
    }

    {
      final GridBagConstraints c = new GridBagConstraints();
      c.insets = insets;
      c.gridy = 1;
      c.anchor = GridBagConstraints.CENTER;
      c.fill = GridBagConstraints.BOTH;
      c.weighty = 0.3;
      c.weightx = 1.0;

      tableStacks.setRowSelectionAllowed(true);
      final JScrollPane scrollPane = new JScrollPane(tableStacks);
      scrollPane.setPreferredSize(new Dimension(500, 150));
      panel.add(scrollPane, c);
    }

    tableException.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
    tableStacks.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);

    final JPanel total = new JPanel(new BorderLayout(0, 0));
    {
      final JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
      toolbar.add(clearBtn);
      //			toolbar.setRequestFocusEnabled(false);

      total.add(toolbar, BorderLayout.NORTH);
      total.add(panel, BorderLayout.CENTER);
    }

    final ActionListener listener = null;
    final JButton closeBtn = App.buildDefaultCloseButton();
    dialog =
        (JFrame)
            App.showCenterPanelMain(
                total,
                0,
                0,
                "Exception List",
                false,
                closeBtn,
                null,
                listener,
                null,
                null,
                false,
                true,
                null,
                true,
                false);
    App.setDisposeListener(
        dialog,
        new DisposeListener() {
          @Override
          public void dispose() {
            dialog = null;
            SingleJFrame.removeJFrame(ExceptionViewer.class.getName());
          }
        });
    SingleJFrame.addJFrame(ExceptionViewer.class.getName(), dialog);
  }

  void updateUI() {
    if (dialog != null) {
      App.invokeLaterUI(refreshTable);
    }
  }

  private static final Thread daemon =
      new Thread("ExceptionViewWriter") {
        @Override
        public void run() {
          Object[] para;
          while (true) {
            synchronized (array) {
              para = array.pollFirst();
              if (para == null) {
                try {
                  array.wait();
                } catch (final Exception e) {
                }
                continue;
              }
            }

            pushIn((String) para[0], (StackTraceElement[]) para[1]);
          }
        }

        public void pushIn(final String paraMessage, final StackTraceElement[] ste) {
          final StringBuilder sb = StringBuilderCacher.getFree();

          calendar.setTimeInMillis(System.currentTimeMillis());
          sb.append(calendar.get(Calendar.HOUR_OF_DAY));
          sb.append(":");
          sb.append(calendar.get(Calendar.MINUTE));
          sb.append(":");
          sb.append(calendar.get(Calendar.SECOND));
          sb.append(".");
          sb.append(calendar.get(Calendar.MILLISECOND));
          sb.append(" ");

          sb.append(paraMessage);

          final String tmp = sb.toString();
          StringBuilderCacher.cycle(sb);

          synchronized (exception) {
            exception.add(tmp);
            stacks.add(ste);
          }

          if (msbViewer == null) {
            msbViewer = new ExceptionViewer();
          }

          msbViewer.show();
        }
      };

  public static void pushIn(final String tmpMsg) {
    if (isPopup) {
      final Object[] para = {tmpMsg, Thread.currentThread().getStackTrace()};
      synchronized (array) {
        array.addLast(para);
        array.notify();
      }
    } else {
      if (LogManager.INI_DEBUG_ON) {
        System.err.println("Exception : " + tmpMsg);
        final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        final int size = ste.length;
        for (int i = 0; i < size; i++) {
          System.err.print("\tat : " + ste[i] + "\n");
        }
      }
    }
  }

  public static void init() {
    CCoreUtil.checkAccess();

    daemon.setPriority(ThreadPriorityManager.LOWEST_PRIORITY);
    daemon.setDaemon(true);
    daemon.start();
  }
}
public class MletHtmlCanvas implements ICanvas, IMletCanvas, HCJSInterface {
  private static final String NO_COMPONENT_HCCODE = "no component hccode : ";

  final int width, height;

  public HTMLMlet mlet;
  public ProjectContext projectContext;
  static final boolean isAndroidServer = ResourceUtil.isAndroidServerPlatform();
  public JFrame frame;
  JScrollPane scrollPane;
  private byte[] screenIDbs;
  private char[] screenIDChars;
  private String screenID, title;
  private DifferTodo differTodo;
  private final boolean isSimu;
  final J2SESession coreSS;

  boolean isForDialog;

  public MletHtmlCanvas(final J2SESession coreSS, final int w, final int h) {
    isSimu = PropertiesManager.isSimu();

    this.coreSS = coreSS;
    this.width = w;
    this.height = h;
  }

  private final void printComp(final Component comp, final int deep) {
    final Rectangle rect = comp.getBounds();
    if (L.isInWorkshop) {
      System.out.println(
          comp.getClass().getName()
              + deep
              + ", x : "
              + rect.x
              + ", y : "
              + rect.y
              + ", w : "
              + rect.width
              + ", h : "
              + rect.height);
    }
    if (comp instanceof Container) {
      final Container container = (Container) comp;
      final int count = container.getComponentCount();
      for (int i = 0; i < count; i++) {
        printComp(container.getComponent(i), deep + 1);
      }
    } else if (comp instanceof JScrollPane) {
      if (L.isInWorkshop) {
        System.out.println("------This is JScrollPane-----");
      }
      printComp(((JScrollPane) comp).getViewport().getView(), deep);
    }
  }

  @Override
  public final void onStart() {
    //		differTodo.executeJS(hcj2seScript);

    //		differTodo.loadJS("msg = \"hello'  abc   \\'world\";");

    // 必须置于上两段初始代码传送之后
    final boolean rtl = LangUtil.isRTL(UserThreadResourceUtil.getMobileLocaleFrom(coreSS));
    if (rtl) {
      differTodo.setLTR(!rtl);
    }

    final ProjResponser projResponser = ServerUIAPIAgent.getProjResponserMaybeNull(projectContext);
    if (projResponser != null) {
      // AddHar下可能为null
      final String dftStyles = (String) projResponser.map.get(HCjar.PROJ_STYLES);
      final String defaultStyles = (dftStyles == null ? "" : dftStyles.trim()); // AddHAR可能出现null
      if (defaultStyles.length() > 0) {
        final String replaceVariable =
            StyleManager.replaceVariable(coreSS, defaultStyles, mlet, projectContext);
        //				L.V = L.O ? false : LogManager.log(replaceVariable);
        differTodo.loadStyles(replaceVariable);
      }
    }

    ServerUIAPIAgent.setDiffTodo(mlet, differTodo);
    //		printComp(scrolPanel, 0);

    // 必须置于notifyInitDone之前,因为有可能增加Mlet级样式和用户setStylesJComponentXX
    ScreenServer.onStartForMlet(coreSS, projectContext, mlet);
  }

  @Override
  public final void onPause() {
    ScreenServer.onPauseForMlet(coreSS, projectContext, mlet);
  }

  @Override
  public final void onResume() {
    ScreenServer.onResumeForMlet(coreSS, projectContext, mlet);
  }

  @Override
  public final void onExit() {
    onExit(false);
  }

  boolean isExitProcced;

  @Override
  public void onExit(final boolean isAutoReleaseAfterGo) {
    synchronized (this) {
      if (isExitProcced) {
        return;
      } else {
        isExitProcced = true;
      }
    }

    if (L.isInWorkshop) {
      L.V = L.O ? false : LogManager.log("onExit MletHtmlCanvas : " + mlet.getTarget());
    }

    ScreenServer.onExitForMlet(coreSS, projectContext, mlet, isAutoReleaseAfterGo);
    MultiUsingManager.exit(
        coreSS, ServerUIAPIAgent.buildScreenID(projectContext.getProjectID(), mlet.getTarget()));
    frame.dispose();
  }

  @Override
  public final void setMlet(
      final J2SESession coreSS, final Mlet mlet, final ProjectContext projectCtx) {
    if (mlet instanceof DialogHTMLMlet) {
      isForDialog = true;
      ((DialogHTMLMlet) mlet).resLock.mletCanvas = this;
    }

    this.mlet = (HTMLMlet) mlet;
    ServerUIAPIAgent.setProjectContext(mlet, projectCtx);
    projectContext = projectCtx;

    frame = new JFrame(); // 不能入Session会导致block showWindowWithoutWarningBanner

    ServerUIAPIAgent.runAndWaitInSessionThreadPool(
        coreSS,
        ServerUIAPIAgent.getProjResponserMaybeNull(projectContext),
        new ReturnableRunnable() {
          @Override
          public Object run() {
            scrollPane =
                new JScrollPane(
                    mlet,
                    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            return null;
          }
        });
    differTodo = new DifferTodo(coreSS, screenID, mlet);
  }

  /**
   * in Android server, AddHarHTMLMlet is NOT need to adapter size.
   *
   * @param scrollPane
   * @return
   */
  public static boolean isForAddHtml(final JScrollPane scrollPane) {
    if (scrollPane != null) {
      final JViewport jviewport = scrollPane.getViewport();
      if (jviewport != null) {
        final Component view = jviewport.getView();
        if (view != null && view instanceof SystemHTMLMlet) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public final void init() { // in user thread
    //		注意:mlet外加JScrollPane时,在Window XP, JRE 7会出现height不能大于1024的情形,Mac无此问题,故关闭JScrollPane
    //		因为如下会发生,所以加4
    //		javax.swing.JScrollPane0, x : 0, y : 0, w : 100, h : 100
    //		javax.swing.JViewport1, x : 2, y : 2, w : 96, h : 96

    frame.setContentPane(scrollPane);

    if (isAndroidServer) {
      scrollPane.setPreferredSize(new Dimension(width, height));
    } else {
      mlet.setPreferredSize(new Dimension(width, height));
    }
    frame.pack(); // 可能重载某些方法
  }

  @Override
  public final void setScreenIDAndTitle(final String screenID, final String title) {
    this.screenID = screenID;
    this.screenIDbs = ByteUtil.getBytes(screenID, IConstant.UTF_8);
    this.screenIDChars = screenID.toCharArray();
    this.title = title;
  }

  @Override
  public boolean isSameScreenID(final byte[] bs, final int offset, final int len) {
    return ByteUtil.isSame(screenIDbs, 0, screenIDbs.length, bs, offset, len);
  }

  @Override
  public boolean isSameScreenIDIgnoreCase(final char[] chars, final int offset, final int len) {
    if (len == screenIDChars.length) {
      for (int i = 0, j = offset; i < len; ) {
        final char c1 = screenIDChars[i++];
        final char c2 = chars[j++];
        if (c1 == c2
            || Character.toUpperCase(c1) == Character.toUpperCase(c2)
            || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
        } else {
          return false;
        }
      }
      return true;
    } else {
      return false;
    }
  }

  private final boolean matchCmd(final byte[] cmdbs, final byte[] bs, final int offset) {
    final int len = cmdbs.length;
    for (int i = 0; i < len; i++) {
      if (cmdbs[i] != bs[offset + i]) {
        return false;
      }
    }
    return true;
  }

  private final int searchNextSplitIndex(final byte[] bs, final int startIdx) {
    final int endIdx = bs.length;
    for (int i = startIdx; i < endIdx; i++) {
      if (bs[i] == JSCore.splitBS[0]) {
        final int endSplitBS = JSCore.splitBS.length;
        if (i + JSCore.splitBS.length < endIdx) {
          boolean isDiff = false;
          for (int j = 1; j < endSplitBS; j++) {
            if (bs[i + j] != JSCore.splitBS[j]) {
              isDiff = true;
              break;
            }
          }
          if (isDiff == false) {
            return i;
          }
        }
      }
    }
    return -1;
  }

  @Override
  public final void actionJSInput(final byte[] bs, final int offset, final int len) {
    if (isForDialog) {
      final DialogHTMLMlet dialog = (DialogHTMLMlet) mlet;

      if (dialog.isContinueProcess() == false) {
        return;
      }
    }

    ServerUIAPIAgent.runAndWaitInSessionThreadPool(
        coreSS,
        ServerUIAPIAgent.getProjResponserMaybeNull(projectContext),
        new ReturnableRunnable() { // 事件的先后性保证
          @Override
          public Object run() {
            actionJSInputInUser(bs, offset, len);
            return null;
          }
        });
  }

  private final void actionJSInputInUser(final byte[] bs, final int offset, final int len) {
    if (isSimu) {
      try {
        L.V =
            L.O
                ? false
                : LogManager.log(
                    "action JS input : " + new String(bs, offset, len, IConstant.UTF_8));
      } catch (final Throwable e1) {
        e1.printStackTrace();
      }
    }

    // -------------------------------------------------------
    // 重要
    // 如果以下增加事件接收,请同步CodeItem.AnonymousClass
    // -------------------------------------------------------
    try {
      if (matchCmd(JSCore.actionExt, bs, offset)) {
        final String cmd = getOneValue(JSCore.actionExt, bs, offset, len);
        actionExt(cmd);
        return;
      } else if (matchCmd(JSCore.clickJButton, bs, offset)) {
        final String id = getOneValue(JSCore.clickJButton, bs, offset, len);
        clickJButton(Integer.parseInt(id));
        return;
      } else if (matchCmd(JSCore.selectComboBox, bs, offset)) {
        final String[] values = getTwoValue(JSCore.selectComboBox, bs, offset, len);
        selectComboBox(Integer.parseInt(values[0]), Integer.parseInt(values[1]));
        return;
      } else if (matchCmd(JSCore.selectSlider, bs, offset)) {
        final String[] values = getTwoValue(JSCore.selectSlider, bs, offset, len);
        selectSlider(Integer.parseInt(values[0]), Integer.parseInt(values[1]));
        return;
      } else if (matchCmd(JSCore.notifyTextFieldValue, bs, offset)) {
        final String[] values = getTwoValue(JSCore.notifyTextFieldValue, bs, offset, len);
        notifyTextFieldValue(Integer.parseInt(values[0]), values[1]);
        return;
      } else if (matchCmd(JSCore.notifyTextAreaValue, bs, offset)) {
        final String[] values = getTwoValue(JSCore.notifyTextAreaValue, bs, offset, len);
        notifyTextAreaValue(Integer.parseInt(values[0]), values[1]);
        return;
      } else if (matchCmd(JSCore.clickJRadioButton, bs, offset)) {
        final String id = getOneValue(JSCore.clickJRadioButton, bs, offset, len);
        clickJRadioButton(Integer.parseInt(id));
        return;
      } else if (matchCmd(JSCore.clickJCheckbox, bs, offset)) {
        final String id = getOneValue(JSCore.clickJCheckbox, bs, offset, len);
        clickJCheckbox(Integer.parseInt(id));
        return;
      } else if (matchCmd(JSCore.mouseReleased, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mouseReleased, bs, offset, len);
        notifyMouseReleased(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      } else if (matchCmd(JSCore.mousePressed, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mousePressed, bs, offset, len);
        notifyMousePressed(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      } else if (matchCmd(JSCore.mouseExited, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mouseExited, bs, offset, len);
        notifyMouseExited(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      } else if (matchCmd(JSCore.mouseEntered, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mouseEntered, bs, offset, len);
        notifyMouseEntered(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      } else if (matchCmd(JSCore.mouseClicked, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mouseClicked, bs, offset, len);
        notifyMouseClicked(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      } else if (matchCmd(JSCore.mouseDragged, bs, offset)) {
        final String[] values = getThreeValue(JSCore.mouseDragged, bs, offset, len);
        notifyMouseDragged(
            Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2]));
        return;
      }
    } catch (final Exception e) {
      ExceptionReporter.printStackTrace(e);
    }

    try {
      final String cmds = new String(bs, offset, len, IConstant.UTF_8);
      final String[] splits = StringUtil.splitToArray(cmds, StringUtil.SPLIT_LEVEL_2_JING);
      LogManager.err("unknow JS input event : " + splits[0] + ", cmd : " + cmds);
    } catch (final Exception e) {
      ExceptionReporter.printStackTrace(e);
    }
  }

  private final void notifyMouseReleased(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseListener[] mlistener = comp.getMouseListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_RELEASED, x, y);
        }
        mlistener[i].mouseReleased(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final MouseEvent buildMouseEvent(
      final Component comp, final int eventID, final int x, final int y) {
    return new MouseEvent(
        comp, eventID, System.currentTimeMillis(), MouseEvent.BUTTON1_MASK, x, y, 1, false) {
      @Override
      public final Point getLocationOnScreen() {
        return super.getLocationOnScreen(); // TODO
      }

      @Override
      public final int getXOnScreen() {
        return super.getXOnScreen(); // TODO
      }

      @Override
      public final int getYOnScreen() {
        return super.getYOnScreen(); // TODO
      }
    };
  }

  private final void notifyMousePressed(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseListener[] mlistener = comp.getMouseListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_PRESSED, x, y);
        }
        mlistener[i].mousePressed(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final void notifyMouseExited(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseListener[] mlistener = comp.getMouseListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_EXITED, x, y);
        }
        mlistener[i].mouseExited(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final void notifyMouseEntered(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseListener[] mlistener = comp.getMouseListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_ENTERED, x, y);
        }
        mlistener[i].mouseEntered(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final void notifyMouseClicked(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseListener[] mlistener = comp.getMouseListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_CLICKED, x, y);
        }
        mlistener[i].mouseClicked(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final void notifyMouseDragged(final int id, final int x, final int y) {
    final Component comp = searchComponentByHcCode(mlet, id);
    if (comp != null) {
      final MouseMotionListener[] mlistener = comp.getMouseMotionListeners();
      MouseEvent event = null;
      for (int i = 0; i < mlistener.length; i++) {
        if (event == null) {
          event = buildMouseEvent(comp, MouseEvent.MOUSE_DRAGGED, x, y);
        }
        mlistener[i].mouseDragged(event);
      }
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  private final Component searchComponentByHcCode(
      final JPanel jpanel, final int hcCode) { // in user thread
    if (differTodo.buildHcCode(jpanel) == hcCode) {
      return jpanel;
    }

    final int compCount = jpanel.getComponentCount();
    for (int i = 0; i < compCount; i++) {
      final Component comp = jpanel.getComponent(i);
      if (comp instanceof JPanel) {
        final Component result = searchComponentByHcCode((JPanel) comp, hcCode);
        if (result != null) {
          return result;
        }
      } else {
        if (differTodo.buildHcCode(comp) == hcCode) {
          return comp;
        }
      }
    }
    return null;
  }

  private final String[] getThreeValue(
      final byte[] actionBS, final byte[] bs, final int offset, final int len) {
    //		try {
    //			final String str = new String(bs, offset, len, IConstant.UTF_8);
    //		} catch (final UnsupportedEncodingException e) {
    //			ExceptionReporter.printStackTrace(e);
    //		}

    final int firstValueIdx = offset + actionBS.length + JSCore.splitBS.length;
    final int secondSplitIdx = searchNextSplitIndex(bs, firstValueIdx);
    final String value1 = new String(bs, firstValueIdx, secondSplitIdx - firstValueIdx);

    final int secondValueIdx = secondSplitIdx + JSCore.splitBS.length;
    final int threeSplitIdx = searchNextSplitIndex(bs, secondValueIdx);
    final String value2 = new String(bs, secondValueIdx, threeSplitIdx - secondValueIdx);

    final int threeValueIdx = threeSplitIdx + JSCore.splitBS.length;
    final String value3 = new String(bs, threeValueIdx, (len + offset) - threeValueIdx);

    final String[] out = {JSCore.decode(value1), JSCore.decode(value2), JSCore.decode(value3)};
    return out;
  }

  private final String[] getTwoValue(
      final byte[] actionBS, final byte[] bs, final int offset, final int len) {
    final int firstValueIdx = offset + actionBS.length + JSCore.splitBS.length;

    final int secondSplitIdx = searchNextSplitIndex(bs, firstValueIdx);
    final String value1 = new String(bs, firstValueIdx, secondSplitIdx - firstValueIdx);

    final int secondValueIdx = secondSplitIdx + JSCore.splitBS.length;
    final String value2 = new String(bs, secondValueIdx, (len + offset) - secondValueIdx);

    final String[] out = {JSCore.decode(value1), JSCore.decode(value2)};
    return out;
  }

  private final String getOneValue(
      final byte[] actionBS, final byte[] bs, final int offset, final int len) {
    final int firstValueIdx = offset + actionBS.length + JSCore.splitBS.length;

    final String id = new String(bs, firstValueIdx, len + offset - firstValueIdx);
    return JSCore.decode(id);
  }

  @Override
  public final void actionExt(final String cmd) {
    // TODO
    System.out.println("actionExt : " + cmd);
  }

  @Override
  public final void clickJButton(final int id) {
    final Component btn = searchComponentByHcCode(mlet, id); // in user thread
    if (btn != null && btn instanceof AbstractButton) {
      final MouseEvent e =
          new MouseEvent(
              btn,
              MouseEvent.MOUSE_CLICKED,
              System.currentTimeMillis(),
              MouseEvent.BUTTON1_MASK,
              0,
              0,
              1,
              false);
      MletSnapCanvas.processClickOnComponent(btn, e);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final void selectSlider(final int id, final int value) {
    final Component slider = searchComponentByHcCode(mlet, id);
    if (slider != null && slider instanceof JSlider) {
      final JSlider sliderBar = (JSlider) slider;

      if (sliderBar.getValue() == value) {
        return;
      }

      sliderBar.setValue(value);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final void selectComboBox(final int id, final int selectedIndex) {
    final Component combo = searchComponentByHcCode(mlet, id);
    if (combo != null && combo instanceof JComboBox) {
      final JComboBox combo2 = (JComboBox) combo;

      if (combo2.getSelectedIndex() == selectedIndex) {
        return;
      }

      //			final Object oldSelected = combo2.getSelectedItem();
      //
      //			//触发ItemEvent[DESELECTED]
      //			if(oldSelected != null){
      //				final ItemEvent e = new ItemEvent(combo2, ItemEvent.ITEM_STATE_CHANGED, oldSelected,
      // ItemEvent.DESELECTED);
      //				MCanvas.dispatchEvent(combo, e);
      //			}
      combo2.setSelectedIndex(selectedIndex);
      //			注意:执行上步时,会在J2SE环境下自动触发下面两事件和上一行事件。
      //			java.awt.event.ItemEvent[ITEM_STATE_CHANGED,item=three,stateChange=SELECTED]
      //			java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=comboBoxChanged,

      //			//触发ItemEvent[SELECTED]
      //			{
      //				final Object newSelected = combo2.getItemAt(selectedIndex);
      //				final ItemEvent e = new ItemEvent(combo2, ItemEvent.ITEM_STATE_CHANGED, newSelected,
      // ItemEvent.SELECTED);
      //				MCanvas.dispatchEvent(combo, e);
      //			}
      //
      //			//触发ActionEvent
      //			MCanvas.doActon(combo);

      //			java.awt.event.ItemEvent[ITEM_STATE_CHANGED,item=one,stateChange=DESELECTED]
      //			java.awt.event.ItemEvent[ITEM_STATE_CHANGED,item=three,stateChange=SELECTED]
      //			java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=comboBoxChanged,
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final void notifyTextFieldValue(final int id, final String value) {
    final Component combo = searchComponentByHcCode(mlet, id);
    if (combo != null && combo instanceof JTextField) {
      final JTextField textField = (JTextField) combo;
      textField.setText(value);
      //			MCanvas.doActon(combo);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final String toString() {
    if (mlet != null) {
      return this.getClass().getSimpleName() + ":" + mlet.getTarget();
    } else {
      return super.toString();
    }
  }

  @Override
  public final void notifyTextAreaValue(final int id, final String value) {
    final Component combo = searchComponentByHcCode(mlet, id);
    if (combo != null
        && combo instanceof JComponent
        && JPanelDiff.isTextMultLinesEditor((JComponent) combo)) {
      final JTextComponent textArea = (JTextComponent) combo;
      textArea.setText(value);
      //			MCanvas.doActon(combo);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final void clickJRadioButton(final int id) {
    final Component combo = searchComponentByHcCode(mlet, id);
    if (combo != null && combo instanceof JRadioButton) {
      final JRadioButton radioButton = (JRadioButton) combo;
      radioButton.doClick();
      //			MCanvas.doActon(combo);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final void clickJCheckbox(final int id) {
    final Component combo = searchComponentByHcCode(mlet, id);
    if (combo != null && combo instanceof JCheckBox) {
      final JCheckBox checkBox = (JCheckBox) combo;
      checkBox.doClick();
      //			MCanvas.doActon(combo);
    } else {
      LogManager.err(NO_COMPONENT_HCCODE + id);
    }
  }

  @Override
  public final Mlet getMlet() {
    return mlet;
  }
}