// This can come on any thread. If we are in the process of reloading
  // the image and determining our state (loading == true) we don't fire
  // preference changed, or repaint, we just reset the fWidth/fHeight as
  // necessary and return. This is ok as we know when loading finishes
  // it will pick up the new height/width, if necessary.
  public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height) {
    if (fImage == null || fImage != img) return false;

    // Bail out if there was an error:
    if ((flags & (ABORT | ERROR)) != 0) {
      fImage = null;
      repaint(0);
      return false;
    }

    // Resize image if necessary:
    short changed = 0;
    if ((flags & ImageObserver.HEIGHT) != 0)
      if (!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) {
        changed |= 1;
      }
    if ((flags & ImageObserver.WIDTH) != 0)
      if (!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) {
        changed |= 2;
      }
    synchronized (this) {
      if ((changed & 1) == 1) {
        fWidth = width;
      }
      if ((changed & 2) == 2) {
        fHeight = height;
      }
      if (loading) {
        // No need to resize or repaint, still in the process of
        // loading.
        return true;
      }
    }
    if (changed != 0) {
      // May need to resize myself, asynchronously:
      if (DEBUG) System.out.println("ImageView: resized to " + fWidth + "x" + fHeight);

      Document doc = getDocument();
      try {
        if (doc instanceof AbstractDocument) {
          ((AbstractDocument) doc).readLock();
        }
        preferenceChanged(this, true, true);
      } finally {
        if (doc instanceof AbstractDocument) {
          ((AbstractDocument) doc).readUnlock();
        }
      }

      return true;
    }

    // Repaint when done or when new pixels arrive:
    if ((flags & (FRAMEBITS | ALLBITS)) != 0) repaint(0);
    else if ((flags & SOMEBITS) != 0) if (sIsInc) repaint(sIncRate);

    return ((flags & ALLBITS) == 0);
  }
  /** Test new Doc */
  public void testNewDoc() {
    AbstractDocument doc = new OpenOfficeDocument();
    try {

      assertEquals("Activate Openoffice", zXType.rc.rcOK.pos, doc.activate().pos);
      assertEquals("Create new blank document", zXType.rc.rcOK.pos, doc.newDoc(null).pos);

    } finally {
      assertEquals("Close document", zXType.rc.rcOK.pos, doc.closeDoc(false).pos);
    }
  }
  /**
   * Tests boTableInit and boTableRowPopulate
   *
   * @throws Exception
   */
  public void testBoTableInit() throws Exception {
    AbstractDocument doc = new OpenOfficeDocument();
    try {
      String strGroup = "*";
      ZXBO objBO = zx.createBO("test/test");
      objBO.setPKValue("12");
      objBO.loadBO();

      assertEquals("Activate Openoffice", zXType.rc.rcOK.pos, doc.activate().pos);
      assertEquals("Open test document", zXType.rc.rcOK.pos, doc.openDoc("c:/tmp/test.doc").pos);

      /** Get the placeholder table. */
      Object objTable = doc.getTable(1, 1, zXType.wordSection.wsPage);
      assertEquals(
          "Setup the table", zXType.rc.rcOK.pos, doc.boTableInit(objTable, objBO, strGroup).pos);

      objTable = doc.getTable(1, 1, zXType.wordSection.wsPage);
      XTextTable xTable = (XTextTable) UnoRuntime.queryInterface(XTextTable.class, objTable);
      XCellRange objRow = (XCellRange) UnoRuntime.queryInterface(XCellRange.class, xTable);
      objRow = objRow.getCellRangeByName("A2:C2");

      assertEquals(
          "Populate table row.",
          zXType.rc.rcOK.pos,
          doc.boTableRowPopulate(objRow, objBO, strGroup).pos);

    } finally {
      assertEquals("Close document", zXType.rc.rcOK.pos, doc.closeDoc(false).pos);
    }
  }
  /** Tests addTableRow */
  public void testAddTableRow() {
    AbstractDocument doc = new OpenOfficeDocument();
    try {

      assertEquals("Activate Openoffice", zXType.rc.rcOK.pos, doc.activate().pos);
      assertEquals("Open test document", zXType.rc.rcOK.pos, doc.openDoc("c:/tmp/test.doc").pos);
      Object objTable = doc.getTable(1, 1, zXType.wordSection.wsPage);
      assertEquals("Add row to table", zXType.rc.rcOK.pos, doc.addTableRow(objTable).pos);

    } finally {
      assertEquals("Close document", zXType.rc.rcOK.pos, doc.closeDoc(false).pos);
    }
  }
  /** Test new Doc */
  public void testSaveDocAs() {
    AbstractDocument doc = new OpenOfficeDocument();
    try {

      assertEquals("Activate Openoffice", zXType.rc.rcOK.pos, doc.activate().pos);
      assertEquals("Open document", zXType.rc.rcOK.pos, doc.openDoc("c:/tmp/test.doc").pos);

      Object objTable = doc.getTable(1, 1, zXType.wordSection.wsPage);
      assertEquals(
          "Set table cell",
          zXType.rc.rcOK.pos,
          doc.setTableCell(objTable, 1, 1, "Hello World!!!").pos);

      assertEquals("Save as", zXType.rc.rcOK.pos, doc.saveDocAs("c:/tmp/test.pdf").pos);

    } finally {
      assertEquals("Close document", zXType.rc.rcOK.pos, doc.closeDoc(false).pos);
    }
  }
  /** Tests populateTableCell */
  public void testPopulateTableCell() {
    AbstractDocument doc = new OpenOfficeDocument();
    try {

      assertEquals("Activate Openoffice", zXType.rc.rcOK.pos, doc.activate().pos);
      assertEquals("Open test document", zXType.rc.rcOK.pos, doc.openDoc("c:/tmp/test.doc").pos);
      Object objTable = doc.getTable(1, 1, zXType.wordSection.wsPage);
      assertEquals(
          "Populate table cell",
          zXType.rc.rcOK.pos,
          doc.populateTableCell(objTable, 1, 1, "(GONE)").pos);

    } finally {
      assertEquals("Close document", zXType.rc.rcOK.pos, doc.closeDoc(false).pos);
    }
  }
  /**
   * Provides a way to determine the next visually represented model location that one might place a
   * caret. Some views may not be visible, they might not be in the same order found in the model,
   * or they just might not allow access to some of the locations in the model.
   *
   * @param v the view to use
   * @param pos the position to convert >= 0
   * @param a the allocated region to render into
   * @param direction the direction from the current position that can be thought of as the arrow
   *     keys typically found on a keyboard. This may be SwingConstants.WEST, SwingConstants.EAST,
   *     SwingConstants.NORTH, or SwingConstants.SOUTH.
   * @return the location within the model that best represents the next location visual position.
   * @exception BadLocationException
   * @exception IllegalArgumentException for an invalid direction
   */
  public int getNextVisualPositionFrom(
      GlyphView v, int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet)
      throws BadLocationException {

    int startOffset = v.getStartOffset();
    int endOffset = v.getEndOffset();
    Segment text;
    AbstractDocument doc;
    boolean viewIsLeftToRight;
    TextHitInfo currentHit, nextHit;

    switch (direction) {
      case View.NORTH:
        break;
      case View.SOUTH:
        break;
      case View.EAST:
        doc = (AbstractDocument) v.getDocument();
        viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset);

        if (startOffset == doc.getLength()) {
          if (pos == -1) {
            biasRet[0] = Position.Bias.Forward;
            return startOffset;
          }
          // End case for bidi text where newline is at beginning
          // of line.
          return -1;
        }
        if (pos == -1) {
          // Entering view from the left.
          if (viewIsLeftToRight) {
            biasRet[0] = Position.Bias.Forward;
            return startOffset;
          } else {
            text = v.getText(endOffset - 1, endOffset);
            char c = text.array[text.offset];
            SegmentCache.releaseSharedSegment(text);
            if (c == '\n') {
              biasRet[0] = Position.Bias.Forward;
              return endOffset - 1;
            }
            biasRet[0] = Position.Bias.Backward;
            return endOffset;
          }
        }
        if (b == Position.Bias.Forward) currentHit = TextHitInfo.afterOffset(pos - startOffset);
        else currentHit = TextHitInfo.beforeOffset(pos - startOffset);
        nextHit = layout.getNextRightHit(currentHit);
        if (nextHit == null) {
          return -1;
        }
        if (viewIsLeftToRight != layout.isLeftToRight()) {
          // If the layout's base direction is different from
          // this view's run direction, we need to use the weak
          // carrat.
          nextHit = layout.getVisualOtherHit(nextHit);
        }
        pos = nextHit.getInsertionIndex() + startOffset;

        if (pos == endOffset) {
          // A move to the right from an internal position will
          // only take us to the endOffset in a left to right run.
          text = v.getText(endOffset - 1, endOffset);
          char c = text.array[text.offset];
          SegmentCache.releaseSharedSegment(text);
          if (c == '\n') {
            return -1;
          }
          biasRet[0] = Position.Bias.Backward;
        } else {
          biasRet[0] = Position.Bias.Forward;
        }
        return pos;
      case View.WEST:
        doc = (AbstractDocument) v.getDocument();
        viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset);

        if (startOffset == doc.getLength()) {
          if (pos == -1) {
            biasRet[0] = Position.Bias.Forward;
            return startOffset;
          }
          // End case for bidi text where newline is at beginning
          // of line.
          return -1;
        }
        if (pos == -1) {
          // Entering view from the right
          if (viewIsLeftToRight) {
            text = v.getText(endOffset - 1, endOffset);
            char c = text.array[text.offset];
            SegmentCache.releaseSharedSegment(text);
            if ((c == '\n') || Character.isSpaceChar(c)) {
              biasRet[0] = Position.Bias.Forward;
              return endOffset - 1;
            }
            biasRet[0] = Position.Bias.Backward;
            return endOffset;
          } else {
            biasRet[0] = Position.Bias.Forward;
            return startOffset;
          }
        }
        if (b == Position.Bias.Forward) currentHit = TextHitInfo.afterOffset(pos - startOffset);
        else currentHit = TextHitInfo.beforeOffset(pos - startOffset);
        nextHit = layout.getNextLeftHit(currentHit);
        if (nextHit == null) {
          return -1;
        }
        if (viewIsLeftToRight != layout.isLeftToRight()) {
          // If the layout's base direction is different from
          // this view's run direction, we need to use the weak
          // carrat.
          nextHit = layout.getVisualOtherHit(nextHit);
        }
        pos = nextHit.getInsertionIndex() + startOffset;

        if (pos == endOffset) {
          // A move to the left from an internal position will
          // only take us to the endOffset in a right to left run.
          text = v.getText(endOffset - 1, endOffset);
          char c = text.array[text.offset];
          SegmentCache.releaseSharedSegment(text);
          if (c == '\n') {
            return -1;
          }
          biasRet[0] = Position.Bias.Backward;
        } else {
          biasRet[0] = Position.Bias.Forward;
        }
        return pos;
      default:
        throw new IllegalArgumentException("Bad direction: " + direction);
    }
    return pos;
  }
  public static void main() {
    // Main
    frame = new JFrame("Java Playground");
    frame.setSize(640, 480);
    // Make sure the divider is properly resized
    frame.addComponentListener(
        new ComponentAdapter() {
          public void componentResized(ComponentEvent c) {
            splitter.setDividerLocation(.8);
          }
        });
    // Make sure the JVM is reset on close
    frame.addWindowListener(
        new WindowAdapter() {
          public void windowClosed(WindowEvent w) {
            new FrameAction().kill();
          }
        });

    // Setting up the keybinding
    // Ctrl+k or Cmd+k -> compile
    bind(KeyEvent.VK_K);

    // Ctrl+e or Cmd+e -> console
    bind(KeyEvent.VK_E);

    // Save, New file, Open file, Print.
    // Currently UNUSED until I figure out how normal java files and playground files will
    // interface.
    bind(KeyEvent.VK_S);
    bind(KeyEvent.VK_N);
    bind(KeyEvent.VK_O);
    bind(KeyEvent.VK_P);

    // Binds the keys to the action defined in the FrameAction class.
    frame.getRootPane().getActionMap().put("console", new FrameAction());

    // The main panel for typing code in.
    text = new JTextPane();
    textScroll = new JScrollPane(text);
    textScroll.setBorder(null);
    textScroll.setPreferredSize(new Dimension(640, 480));

    // Document with syntax highlighting. Currently unfinished.
    doc = text.getStyledDocument();
    doc.addDocumentListener(
        new DocumentListener() {
          public void changedUpdate(DocumentEvent d) {}

          public void insertUpdate(DocumentEvent d) {}

          public void removeUpdate(DocumentEvent d) {}
        });

    ((AbstractDocument) doc).setDocumentFilter(new NewLineFilter());

    // The output log; a combination compiler warning/error/runtime error/output log.
    outputText = new JTextPane();
    outputScroll = new JScrollPane(outputText);
    outputScroll.setBorder(null);

    // "Constant" for the error font
    error = new SimpleAttributeSet();
    error.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.TRUE);
    error.addAttribute(StyleConstants.Foreground, Color.RED);

    // "Constant" for the warning message font
    warning = new SimpleAttributeSet();
    warning.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.TRUE);
    warning.addAttribute(StyleConstants.Foreground, Color.PINK);

    // "Constant" for the debugger error font
    progErr = new SimpleAttributeSet();
    progErr.addAttribute(StyleConstants.Foreground, Color.BLUE);

    // Print streams to redirect System.out and System.err.
    out = new TextOutputStream(outputText, null);
    err = new TextOutputStream(outputText, error);
    System.setOut(new PrintStream(out));
    System.setErr(new PrintStream(err));

    // Sets up the output log
    outputText.setEditable(false);
    outputScroll.setVisible(true);

    // File input/output setup
    chooser = new JFileChooser();

    // Setting up miscellaneous stuff
    compiler = ToolProvider.getSystemJavaCompiler();
    JVMrunning = false;
    redirectErr = null;
    redirectOut = null;
    redirectIn = null;

    // Sets up the splitter pane and opens the program up
    splitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textScroll, outputScroll);
    consoleDisplayed = false;
    splitter.remove(outputScroll); // Initially hides terminal until it is needed
    splitter.setOneTouchExpandable(true);
    frame.add(splitter);
    frame.setVisible(true);

    // Sets the divider to the proper one, for debugging
    // splitter.setDividerLocation(.8);
  }
  public TestReport runImpl() throws Exception {
    Handler h = new Handler();
    TestReport report = null;

    // cdata-sections == false
    Document doc = newSVGDoc();
    DOMConfiguration conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("cdata-sections", Boolean.FALSE);
    Element e = doc.getDocumentElement();
    e.appendChild(doc.createTextNode("abc"));
    e.appendChild(doc.createCDATASection("def"));
    e.appendChild(doc.createTextNode("ghi"));
    ((AbstractDocument) doc).normalizeDocument();
    if (!(e.getFirstChild().getNodeType() == Node.TEXT_NODE
        && e.getFirstChild().getNodeValue().equals("abcdefghi")
        && e.getFirstChild() == e.getLastChild())) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "cdata-sections == false");
    }

    // comments == false
    doc = newSVGDoc();
    conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("comments", Boolean.FALSE);
    e = doc.getDocumentElement();
    e.appendChild(doc.createTextNode("abc"));
    e.appendChild(doc.createComment("def"));
    e.appendChild(doc.createTextNode("ghi"));
    ((AbstractDocument) doc).normalizeDocument();
    if (!(e.getFirstChild().getNodeType() == Node.TEXT_NODE
        && e.getFirstChild().getNodeValue().equals("abcghi")
        && e.getFirstChild() == e.getLastChild())) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "comments == false");
    }

    // element-content-whitespace == false
    doc = newSVGDoc();
    conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("element-content-whitespace", Boolean.FALSE);
    e = doc.getDocumentElement();
    e.appendChild(doc.createTextNode("    "));
    e.appendChild(doc.createElementNS(SVG_NAMESPACE_URI, "g"));
    e.appendChild(doc.createTextNode("    "));
    ((AbstractDocument) doc).normalizeDocument();
    if (!(e.getFirstChild().getNodeType() == Node.ELEMENT_NODE
        && e.getFirstChild().getNodeName().equals("g")
        && e.getFirstChild() == e.getLastChild())) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry(
          "DOMConfiguration parameter", "element-content-whitespace == false");
    }

    // split-cdata-sections == true
    doc = newSVGDoc();
    conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("split-cdata-sections", Boolean.TRUE);
    conf.setParameter("error-handler", h);
    e = doc.getDocumentElement();
    e.appendChild(doc.createCDATASection("before ]]> after"));
    ((AbstractDocument) doc).normalizeDocument();
    if (!(e.getFirstChild().getNodeType() == Node.CDATA_SECTION_NODE
        && e.getFirstChild().getNodeValue().equals("before ]]")
        && e.getFirstChild().getNextSibling().getNodeType() == Node.CDATA_SECTION_NODE
        && e.getFirstChild().getNextSibling().getNodeValue().equals("> after")
        && e.getFirstChild().getNextSibling() == e.getLastChild()
        && h.get("cdata-sections-splitted") == 1)) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "split-cdata-sections == true");
    }

    // well-formed
    doc = newSVGDoc();
    ((AbstractDocument) doc).setStrictErrorChecking(false);
    conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("error-handler", h);
    e = doc.getDocumentElement();
    e.appendChild(doc.createComment("before -- after"));
    e.appendChild(doc.createComment("ends in a dash -"));
    e.setAttribute("*", "blah");
    e.appendChild(doc.createProcessingInstruction("abc", "def?>"));
    ((AbstractDocument) doc).normalizeDocument();
    if (!(h.get("wf-invalid-character-in-node-name") == 1 && h.get("wf-invalid-character") == 3)) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "well-formed == true");
    }

    // namespaces
    doc = newDoc();
    e = doc.createElementNS(null, "root");
    doc.appendChild(e);
    Element e2 = doc.createElementNS(null, "parent");
    e.appendChild(e2);
    e2.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:ns", "http://www.example.org/ns1");
    e2.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:bar", "http://www.example.org/ns2");
    Element e3 = doc.createElementNS("http://www.example.org/ns1", "ns:child1");
    e2.appendChild(e3);
    e3.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:ns", "http://www.example.org/ns2");
    e3 = doc.createElementNS("http://www.example.org/ns2", "ns:child2");
    e2.appendChild(e3);
    ((AbstractDocument) doc).normalizeDocument();
    Attr a = e3.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "ns");
    if (!(a != null
        && a.getNodeName().equals("xmlns:ns")
        && a.getNodeValue().equals("http://www.example.org/ns2"))) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "namespaces == true, test 1");
    }

    doc = newDoc();
    e = doc.createElementNS(null, "root");
    doc.appendChild(e);
    e2 = doc.createElementNS("http://www.example.org/ns1", "ns:child1");
    e.appendChild(e2);
    e2.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:ns", "http://www.example.org/ns1");
    e3 = doc.createElementNS("http://www.example.org/ns1", "ns:child2");
    e2.appendChild(e3);
    e2 =
        (Element)
            ((AbstractDocument) doc).renameNode(e2, "http://www.example.org/ns2", "ns:child1");
    ((AbstractDocument) doc).normalizeDocument();
    a = e2.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "ns");
    Attr a2 = e3.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "ns");
    if (!(a != null
        && a.getNodeName().equals("xmlns:ns")
        && a.getNodeValue().equals("http://www.example.org/ns2")
        && a2 != null
        && a2.getNodeName().equals("xmlns:ns")
        && a2.getNodeValue().equals("http://www.example.org/ns1"))) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "namespaces == true, test 2");
    }

    doc = newDoc();
    e = doc.createElementNS(null, "root");
    doc.appendChild(e);
    e2 = doc.createElementNS("http://www.example.org/ns1", "child1");
    e.appendChild(e2);
    e2.setAttributeNS("http://www.example.org/ns2", "blah", "hi");
    ((AbstractDocument) doc).normalizeDocument();
    a = e2.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "xmlns");
    a2 = e2.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "NS1");
    if (!(a != null
        && a.getNodeValue().equals("http://www.example.org/ns1")
        && a2 != null
        && a2.getNodeValue().equals("http://www.example.org/ns2"))) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "namespaces == true, test 3");
    }

    // namespace-declarations == false
    doc = newDoc();
    e = doc.createElementNS(null, "ex:root");
    e.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:ex", "http://www.example.org/ns1");
    conf = ((AbstractDocument) doc).getDomConfig();
    conf.setParameter("namespace-declarations", Boolean.FALSE);
    doc.appendChild(e);
    ((AbstractDocument) doc).normalizeDocument();
    if (!(e.getAttributeNodeNS(XMLNS_NAMESPACE_URI, "ex") == null)) {
      if (report == null) {
        report = reportError("Document.normalizeDocument test failed");
      }
      report.addDescriptionEntry("DOMConfiguration parameter", "namespace-declarations == false");
    }

    if (report == null) {
      return reportSuccess();
    }
    return report;
  }