Subject: CVS update: /editor/, /editor/lib/, /editor/lib/bridge/, /editor/lib/bridge/nbproject/, /editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/, /editor/lib/nbproject/, /editor/lib/test/, /editor/lib2/, /editor/lib2/nbproject/, /editor/lib2/src/org/netbeans/modules/editor/lib2/, /editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/, /editor/lib2/src/org/netbeans/modules/editor/lib2/resources/, /editor/lib2/src/org/netbeans/modules/editor/lib2/search/, /editor/lib2/src/org/netbeans/spi/editor/, /editor/lib2/src/org/netbeans/spi/editor/highlighting/, /editor/lib2/src/org/netbeans/spi/editor/highlighting/support/, /editor/lib2/test/, /editor/lib2/test/unit/src/META-INF/services/, /editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/, /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/, /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/, /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/data/, /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/, /editor/libsrc/org/netbeans/editor/, /editor/libsrc/org/netbeans/editor/ext/, /editor/nbproject/, /editor/options/src/org/netbeans/modules/options/colors/, /editor/settings/nbproject/, /editor/settings/src/org/netbeans/api/editor/settings/, /editor/src/META-INF/services/, /editor/src/org/netbeans/modules/editor/, /editor/src/org/netbeans/modules/editor/impl/, /nbbuild/, /nbbuild/javadoctools/, /nbbuild/nbproject/, /ide/golden/, /diff/src/org/netbeans/modules/diff/builtin/visualizer/, /diff/src/org/netbeans/modules/merge/builtin/visualizer/, /html/editor/lib/nbproject/, /html/editor/nbproject/, /html/editor/src/org/netbeans/modules/editor/html/, /java/editor/nbproject/, /java/editor/src/org/netbeans/modules/editor/java/, /languages/engine/nbproject/, /languages/engine/src/org/netbeans/modules/languages/, /versioncontrol/src/org/netbeans/modules/versioning/diff/, /web/jspsyntax/nbproject/, /web/jspsyntax/src/org/netbeans/modules/web/core/syntax/, /xml/text-edit/nbproject/, /xml/text-edit/src/org/netbeans/modules/xml/text/syntax/ From: vstejskal@netbeans.org Date: 19 Jan 2007 05:21:43 -0000 To: cvs@ide.netbeans.org, cvs@versioncontrol.netbeans.org, cvs@java.netbeans.org, cvs@editor.netbeans.org, cvs@xml.netbeans.org, cvs@diff.netbeans.org, cvs@nbbuild.netbeans.org, cvs@web.netbeans.org, cvs@languages.netbeans.org, cvs@html.netbeans.org User: vstejskal Date: 2007/01/18 21:21:43 Removed: editor/editor_api-branch-info.txt Added: editor/lib/bridge/.cvsignore editor/lib/bridge/build.xml editor/lib/bridge/manifest.mf editor/lib/bridge/nbproject/.cvsignore editor/lib/bridge/nbproject/project.properties editor/lib/bridge/nbproject/project.xml editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/Bundle.properties editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/GuardedBlocksHighlighting.java editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/HLFactory.java editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/HighlightingDrawLayer.java editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/Installer.java editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/NonLexerSyntaxHighlighting.java editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/layer.xml editor/lib/test/.cvsignore editor/lib/test/build.xml editor/lib/test/cfg-unit.xml editor/lib2/.cvsignore editor/lib2/apichanges.xml editor/lib2/arch.xml editor/lib2/build.xml editor/lib2/manifest.mf editor/lib2/nbproject/.cvsignore editor/lib2/nbproject/project.properties editor/lib2/nbproject/project.xml editor/lib2/src/org/netbeans/modules/editor/lib2/Acceptor.java editor/lib2/src/org/netbeans/modules/editor/lib2/AcceptorFactory.java editor/lib2/src/org/netbeans/modules/editor/lib2/Bundle.properties editor/lib2/src/org/netbeans/modules/editor/lib2/ComponentUtils.java editor/lib2/src/org/netbeans/modules/editor/lib2/DialogSupport.java editor/lib2/src/org/netbeans/modules/editor/lib2/DocUtils.java editor/lib2/src/org/netbeans/modules/editor/lib2/DocumentsJumpList.java editor/lib2/src/org/netbeans/modules/editor/lib2/DocumentsRegistry.java editor/lib2/src/org/netbeans/modules/editor/lib2/EditorImplementation.java editor/lib2/src/org/netbeans/modules/editor/lib2/KeyEventBlocker.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/AbstractOffsetGapList.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/BlockHighlighting.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/CaretRowHighlighting.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/CompoundHighlightsContainer.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/Factory.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingManager.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingSpiPackageAccessor.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsLayerAccessor.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsLayerFilter.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/OffsetGapList.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/ProxyHighlightsContainer.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/SyntaxHighlighting.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/TextSearchHighlighting.java editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/TextSelectionHighlighting.java editor/lib2/src/org/netbeans/modules/editor/lib2/resources/layer.xml editor/lib2/src/org/netbeans/modules/editor/lib2/search/Bundle.properties editor/lib2/src/org/netbeans/modules/editor/lib2/search/DocumentFinder.java editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogPanel.form editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogPanel.java editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogSupport.java editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindSupport.java editor/lib2/src/org/netbeans/spi/editor/DialogFactory.java editor/lib2/src/org/netbeans/spi/editor/EditorImplementationProvider.java editor/lib2/src/org/netbeans/spi/editor/highlighting/AttributesUtilities.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsChangeEvent.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsChangeListener.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsContainer.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsLayer.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsLayerFactory.java editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsSequence.java editor/lib2/src/org/netbeans/spi/editor/highlighting/ZOrder.java editor/lib2/src/org/netbeans/spi/editor/highlighting/package.html editor/lib2/src/org/netbeans/spi/editor/highlighting/support/AbstractHighlightsContainer.java editor/lib2/src/org/netbeans/spi/editor/highlighting/support/OffsetsBag.java editor/lib2/src/org/netbeans/spi/editor/highlighting/support/PositionsBag.java editor/lib2/test/.cvsignore editor/lib2/test/build.xml editor/lib2/test/cfg-unit.xml editor/lib2/test/unit/src/META-INF/services/org.netbeans.spi.editor.mimelookup.MimeDataProvider editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/CompoundHighlightsContainerTest.java editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingManagerTest.java editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/MemoryMimeDataProvider.java editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/ProxyHighlightsContainerTest.java editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/SyntaxHighlightingTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/AttributesUtilitiesTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/ZOrderTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/PositionsBagFindHighlightTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/PositionsBagMemoryTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/SimplePosition.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/SyntaxHighlightingTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/data/VeryBigClassThatWasStolenAndDoesNotCompile-java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/MergingOffsetsBagTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/MergingPositionsBagTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/OffsetsBagTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/PositionsBagRandomTest.java editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/PositionsBagTest.java editor/src/META-INF/services/org.netbeans.spi.editor.DialogFactory editor/src/META-INF/services/org.netbeans.spi.editor.EditorImplementationProvider editor/src/org/netbeans/modules/editor/impl/NbDialogFactory.java editor/src/org/netbeans/modules/editor/impl/NbEditorImplementationProvider.java Modified: editor/lib/apichanges.xml editor/lib/manifest.mf editor/lib/nbproject/project.properties editor/lib/nbproject/project.xml editor/libsrc/org/netbeans/editor/ActionFactory.java editor/libsrc/org/netbeans/editor/BaseCaret.java editor/libsrc/org/netbeans/editor/BaseDocument.java editor/libsrc/org/netbeans/editor/BaseTextUI.java editor/libsrc/org/netbeans/editor/DialogSupport.java editor/libsrc/org/netbeans/editor/DocumentFinder.java editor/libsrc/org/netbeans/editor/DrawLayer.java editor/libsrc/org/netbeans/editor/DrawLayerFactory.java editor/libsrc/org/netbeans/editor/DrawLayerList.java editor/libsrc/org/netbeans/editor/EditorUI.java editor/libsrc/org/netbeans/editor/FindSupport.java editor/libsrc/org/netbeans/editor/GuardedDocument.java editor/libsrc/org/netbeans/editor/ImplementationProvider.java editor/libsrc/org/netbeans/editor/JumpList.java editor/libsrc/org/netbeans/editor/MarkFactory.java editor/libsrc/org/netbeans/editor/Registry.java editor/libsrc/org/netbeans/editor/ext/ExtCaret.java editor/libsrc/org/netbeans/editor/ext/ExtFinderFactory.java editor/libsrc/org/netbeans/editor/ext/FindDialogPanel.java editor/libsrc/org/netbeans/editor/ext/FindDialogSupport.java editor/libsrc/org/netbeans/editor/ext/GotoDialogSupport.java editor/nbproject/project.xml editor/options/src/org/netbeans/modules/options/colors/ColorModel.java editor/settings/nbproject/project.xml editor/settings/src/org/netbeans/api/editor/settings/FontColorNames.java editor/src/org/netbeans/modules/editor/EditorModule.java editor/src/org/netbeans/modules/editor/NbDialogSupport.java editor/src/org/netbeans/modules/editor/NbImplementationProvider.java nbbuild/cluster.properties nbbuild/javadoctools/links.xml nbbuild/javadoctools/properties.xml nbbuild/javadoctools/replaces.xml nbbuild/nbproject/project.xml ide/golden/cluster-deps.txt ide/golden/deps.txt ide/golden/files-layout.txt ide/golden/impl-deps.txt ide/golden/moduleconfigs.txt ide/golden/modules.txt ide/golden/public-packages.txt diff/src/org/netbeans/modules/diff/builtin/visualizer/DiffPanel.java diff/src/org/netbeans/modules/diff/builtin/visualizer/DiffViewImpl.java diff/src/org/netbeans/modules/merge/builtin/visualizer/MergePanel.java html/editor/lib/nbproject/project.xml html/editor/nbproject/project.xml html/editor/src/org/netbeans/modules/editor/html/HTMLKit.java java/editor/nbproject/project.xml java/editor/src/org/netbeans/modules/editor/java/JavaKit.java languages/engine/nbproject/project.xml languages/engine/src/org/netbeans/modules/languages/LanguagesEditorKit.java versioncontrol/src/org/netbeans/modules/versioning/diff/DiffTooltipContentPanel.java web/jspsyntax/nbproject/project.xml web/jspsyntax/src/org/netbeans/modules/web/core/syntax/JSPKit.java xml/text-edit/nbproject/project.xml xml/text-edit/src/org/netbeans/modules/xml/text/syntax/XMLKit.java Log: Integrating Highlighting SPI from editor_api_delivery_200701191221 File Changes: Directory: /editor/ =================== File [removed]: editor_api-branch-info.txt Directory: /editor/lib/ ======================= File [changed]: apichanges.xml Url: http://editor.netbeans.org/source/browse/editor/lib/apichanges.xml?r1=1.3&r2=1.4 Delta lines: +16 -0 -------------------- --- apichanges.xml 5 Jan 2007 03:30:38 -0000 1.3 +++ apichanges.xml 19 Jan 2007 05:21:10 -0000 1.4 @@ -82,6 +82,22 @@ + + Deprecating the use of DrawLayer + + + + + +

+ Using DrawLayer and related classes and methods + has been deprecated in favor of the new Highlighting SPI in + the editor/lib2 module. For more details see + Highlighting SPI. +

+
+
+ Adding Coloring.fromAttributeSet method File [changed]: manifest.mf Url: http://editor.netbeans.org/source/browse/editor/lib/manifest.mf?r1=1.9&r2=1.10 Delta lines: +1 -1 ------------------- --- manifest.mf 9 Apr 2005 16:08:11 -0000 1.9 +++ manifest.mf 19 Jan 2007 05:21:11 -0000 1.10 @@ -2,4 +2,4 @@ OpenIDE-Module: org.netbeans.modules.editor.lib/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib/Bundle.properties OpenIDE-Module-Implementation-Version: 1 - +OpenIDE-Module-Needs: org.netbeans.modules.editor.oldlibbridge Directory: /editor/lib/bridge/ ============================== File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- build File [added]: build.xml Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/build.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 5 -------------- Builds, tests, and runs the project org.netbeans.modules.editor.oldlibbridge File [added]: manifest.mf Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/manifest.mf?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 7 -------------- Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor.oldlibbridge/1 OpenIDE-Module-Layer: org/netbeans/modules/editor/oldlibbridge/layer.xml OpenIDE-Module-Install: org/netbeans/modules/editor/oldlibbridge/Installer.class OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/oldlibbridge/Bundle.properties OpenIDE-Module-Provides: org.netbeans.modules.editor.oldlibbridge Directory: /editor/lib/bridge/nbproject/ ======================================== File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/nbproject/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- private File [added]: project.properties Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/nbproject/project.properties?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 4 -------------- is.eager=true javac.compilerargs=-Xlint:unchecked javac.source=1.5 spec.version.base=1.0 File [added]: project.xml Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/nbproject/project.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 97 --------------- org.netbeans.modules.apisupport.project org.netbeans.modules.editor.oldlibbridge org.netbeans.modules.editor.lib 1 1.10 org.netbeans.modules.editor.lib2 1 org.netbeans.modules.editor.mimelookup 1 1.6 org.netbeans.modules.editor.settings 1 1.5 org.netbeans.modules.lexer 2 1.11 org.openide.filesystems 7.0 org.openide.loaders 5.11 org.openide.modules 7.2 org.openide.nodes 6.8 org.openide.util 7.3 Directory: /editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/ =========================================================================== File [added]: Bundle.properties Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/Bundle.properties?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- OpenIDE-Module-Name=Old and new library bridge File [added]: GuardedBlocksHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/GuardedBlocksHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 229 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.oldlibbridge; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.editor.GuardedDocument; import org.netbeans.editor.MarkBlock; import org.netbeans.editor.MarkBlockChain; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; /** * * @author Vita Stejskal */ public final class GuardedBlocksHighlighting extends AbstractHighlightsContainer implements LookupListener { private static final Logger LOG = Logger.getLogger(GuardedBlocksHighlighting.class.getName()); public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.oldlibbridge.GuardedBlocksHighlighting"; //NOI18N private final Document document; private final MimePath mimePath; private final Lookup.Result lookupResult; private long version = 0; private AttributeSet attribs = null; /** Creates a new instance of NonLexerSytaxHighlighting */ public GuardedBlocksHighlighting(Document document, String mimeType) { this.document = document; this.mimePath = MimePath.parse(mimeType); this.lookupResult = MimeLookup.getLookup(mimePath).lookup(new Lookup.Template(FontColorSettings.class)); this.lookupResult.addLookupListener(this); } public HighlightsSequence getHighlights(int startOffset, int endOffset) { synchronized (this) { if (document instanceof GuardedDocument) { MarkBlockChain guardedBlocks = ((GuardedDocument) document).getGuardedBlockChain(); return new HSImpl(version, guardedBlocks, startOffset, endOffset); } else { return HighlightsSequence.EMPTY; } } } // ---------------------------------------------------------------------- // LookupListener implementation // ---------------------------------------------------------------------- public void resultChanged(LookupEvent ev) { synchronized (this) { attribs = null; version++; } document.render(new Runnable() { public void run() { fireHighlightsChange(0, Integer.MAX_VALUE); } }); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private final class HSImpl implements HighlightsSequence { private final long version; private final MarkBlockChain guardedBlocks; private final int startOffset; private final int endOffset; private boolean init = false; private MarkBlock block; public HSImpl(long version, MarkBlockChain guardedBlocks, int startOffset, int endOffset) { this.version = version; this.guardedBlocks = guardedBlocks; this.startOffset = startOffset; this.endOffset = endOffset; } public boolean moveNext() { if (!init) { init = true; block = guardedBlocks.getChain(); while(null != block) { if (block.getEndOffset() > startOffset) { break; } if (LOG.isLoggable(Level.FINE)) { LOG.fine("Skipping block: " + block + //NOI18N ", blockStart = " + block.getStartOffset() + //NOI18N ", blockEnd = " + block.getEndOffset() + //NOI18N ", startOffset = " + startOffset + //NOI18N ", endOffset = " + endOffset //NOI18N ); } block = block.getNext(); } } else if (block != null) { block = block.getNext(); } if (block != null && block.getStartOffset() > endOffset) { block = null; } if (LOG.isLoggable(Level.FINE)) { if (block != null) { LOG.fine("Next block: " + block + //NOI18N ", blockStart = " + block.getStartOffset() + //NOI18N ", blockEnd = " + block.getEndOffset() + //NOI18N ", startOffset = " + startOffset + //NOI18N ", endOffset = " + endOffset //NOI18N ); } else { LOG.fine("Next block: null"); //NOI18N } } return block != null; } public int getStartOffset() { synchronized (GuardedBlocksHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (block == null) { throw new NoSuchElementException(); } return Math.max(block.getStartOffset(), startOffset); } } public int getEndOffset() { synchronized (GuardedBlocksHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (block == null) { throw new NoSuchElementException(); } return Math.min(block.getEndOffset(), endOffset); } } public AttributeSet getAttributes() { synchronized (GuardedBlocksHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (block == null) { throw new NoSuchElementException(); } if (attribs == null) { Collection allFcs = lookupResult.allInstances(); if (!allFcs.isEmpty()) { FontColorSettings fcs = allFcs.iterator().next(); attribs = fcs.getFontColors(FontColorNames.GUARDED_COLORING); } if (attribs == null) { attribs = SimpleAttributeSet.EMPTY; } else { attribs = AttributesUtilities.createImmutable( attribs, AttributesUtilities.createImmutable(ATTR_EXTENDS_EOL, Boolean.TRUE) ); } } return attribs; } } private void checkVersion() { if (this.version != GuardedBlocksHighlighting.this.version) { throw new ConcurrentModificationException(); } } } // End of HSImpl class } File [added]: HLFactory.java Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/HLFactory.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 77 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.oldlibbridge; import java.util.ArrayList; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.modules.editor.lib2.highlighting.CaretRowHighlighting; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.ZOrder; /** * * @author Vita Stejskal */ public final class HLFactory implements HighlightsLayerFactory { /** Creates a new instance of HLFactory */ public HLFactory() { } public HighlightsLayer[] createLayers(HighlightsLayerFactory.Context context) { ArrayList layers = new ArrayList(); final Document d = context.getDocument(); final JTextComponent c = context.getComponent(); final String mimeType = getMimeType(c, d); layers.add(HighlightsLayer.create( GuardedBlocksHighlighting.LAYER_TYPE_ID, ZOrder.BOTTOM_RACK.belowLayers(CaretRowHighlighting.LAYER_TYPE_ID), true, // fixedSize new GuardedBlocksHighlighting(d, mimeType) )); if (TokenHierarchy.get(context.getDocument()) == null) { // There is no lexer yet, we will use this layer for backwards compatibility layers.add(HighlightsLayer.create( NonLexerSyntaxHighlighting.LAYER_TYPE_ID, ZOrder.SYNTAX_RACK, true, // fixedSize new NonLexerSyntaxHighlighting(d, mimeType) )); } return layers.toArray(new HighlightsLayer[layers.size()]); } private static String getMimeType(JTextComponent c, Document d) { String mimeType = (String) d.getProperty("mimeType"); //NOI18N if (mimeType == null) { mimeType = c.getUI().getEditorKit(c).getContentType(); } return mimeType == null ? "" : mimeType; //NOI18N } } File [added]: HighlightingDrawLayer.java Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/HighlightingDrawLayer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 464 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.oldlibbridge; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Coloring; import org.netbeans.editor.DrawContext; import org.netbeans.editor.DrawLayer; import org.netbeans.editor.EditorUI; import org.netbeans.editor.MarkFactory; import org.netbeans.editor.Registry; import org.netbeans.editor.Utilities; import org.netbeans.editor.ext.ExtCaret; import org.netbeans.modules.editor.lib2.highlighting.CaretRowHighlighting; import org.netbeans.modules.editor.lib2.highlighting.Factory; import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager; import org.netbeans.modules.editor.lib2.highlighting.HighlightingSpiPackageAccessor; import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerAccessor; import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerFilter; import org.netbeans.modules.editor.lib2.highlighting.TextSearchHighlighting; import org.netbeans.modules.editor.lib2.highlighting.TextSelectionHighlighting; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.openide.util.RequestProcessor; /** * * @author vita */ public class HighlightingDrawLayer extends DrawLayer.AbstractLayer implements HighlightsChangeListener { private static final Logger LOG = Logger.getLogger(HighlightingDrawLayer.class.getName()); private static final String LAYER_A_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/A"; //NOI18N // Using the original name for the caret row highlighting, some clients use it to remove the layer. private static final String LAYER_B_NAME = ExtCaret.HIGHLIGHT_ROW_LAYER_NAME; private static final String LAYER_C_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/C"; //NOI18N public static final Injector INJECTOR = new Injector(); private static final HighlightsLayerFilter FILTER_A = new HighlightsLayerFilter() { public List filterLayers(List layers) { ArrayList filteredLayers = new ArrayList(); for(HighlightsLayer layer : layers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); if (TextSelectionHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId()) || Factory.BLOCK_SEARCH_LAYER.equals(layerAccessor.getLayerTypeId()) || TextSearchHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId()) || Factory.INC_SEARCH_LAYER.equals(layerAccessor.getLayerTypeId()) ) { filteredLayers.add(layer); } } return filteredLayers; } }; // End of FILTER_A constant private static final HighlightsLayerFilter FILTER_B = new HighlightsLayerFilter() { public List filterLayers(List layers) { ArrayList filteredLayers = new ArrayList(); for(HighlightsLayer layer : layers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) { filteredLayers.add(layer); } } return filteredLayers; } }; // End of FILTER_B constant private static final HighlightsLayerFilter FILTER_C = new HighlightsLayerFilter() { public List filterLayers(List layers) { ArrayList filteredLayers = new ArrayList(); for(HighlightsLayer layer : layers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); if (!TextSelectionHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId()) && !Factory.BLOCK_SEARCH_LAYER.equals(layerAccessor.getLayerTypeId()) && !TextSearchHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId()) & !Factory.INC_SEARCH_LAYER.equals(layerAccessor.getLayerTypeId()) && !CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId()) ) { filteredLayers.add(layer); } } return filteredLayers; } }; // End of FILTER_B constant private final WeakReference paneRef; private final HighlightsLayerFilter filter; private final String mimeType; private final HighlightsContainer highlights; private AttributeSet lastAttributeSet = null; // The end-of-line attributes cache private AttributeSet lastEOLAttribs = null; private AttributeSet lastELAttribs = null; private boolean theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false; /** Creates a new instance of HighlightingDrawLayer */ public HighlightingDrawLayer(String name, JTextComponent pane, HighlightsLayerFilter filter) { super(name); this.paneRef = new WeakReference(pane); this.filter = filter; this.mimeType = pane.getUI().getEditorKit(pane).getContentType(); HighlightingManager hm = HighlightingManager.getInstance(); this.highlights = hm.getHighlights(pane, filter); this.highlights.addHighlightsChangeListener(this); } public void init(DrawContext ctx) { super.init(ctx); lastAttributeSet = null; // Reset the end-of-line attributes cache lastEOLAttribs = null; lastELAttribs = null; theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false; } public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) { if (highlights != null) { return processOffset(ctx, false); } else { return false; } } public void updateContext(DrawContext ctx) { if (highlights != null) { if (ctx.isEOL() && ctx.isBOL()) { if (extendsEmptyLine() && !theLittleSpitAtTheBeginningOfAnEmptyLineDrawn) { theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = true; Coloring coloring = Coloring.fromAttributeSet(lastELAttribs); coloring.apply(ctx); } else { if (extendsEOL()) { Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs); coloring.apply(ctx); } } } else if (ctx.isEOL()) { if (extendsEOL()) { Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs); coloring.apply(ctx); } } else { processOffset(ctx, true); } } } public boolean extendsEOL() { if (lastEOLAttribs == null && lastAttributeSet != null) { List allSets = HighlightingSpiPackageAccessor.get().dismantle(lastAttributeSet); AttributeSet [] arr = filter(allSets); lastEOLAttribs = arr[0]; lastELAttribs = arr[1]; } return lastEOLAttribs != null && lastEOLAttribs != SimpleAttributeSet.EMPTY; } public boolean extendsEmptyLine() { if (lastELAttribs == null && lastAttributeSet != null) { List allSets = HighlightingSpiPackageAccessor.get().dismantle(lastAttributeSet); AttributeSet [] arr = filter(allSets); lastEOLAttribs = arr[0]; lastELAttribs = arr[1]; } return lastELAttribs != null && lastELAttribs != SimpleAttributeSet.EMPTY; } // ---------------------------------------------------------------------- // HighlightsChangeListener implementation // ---------------------------------------------------------------------- public void highlightChanged(HighlightsChangeEvent event) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("BRIDGE-LAYER: changed area [" + event.getStartOffset() + ", " + event.getEndOffset() + "]"); //NOI18N // LOG.log(Level.FINE, "Dumping highlights: {"); // HighlightsSequence seq = highlights.getHighlights(0, Integer.MAX_VALUE); // while(seq.moveNext()) { // StringBuilder sb = new StringBuilder(); // sb.append(" <"); // sb.append(seq.getStartOffset()); // sb.append(", "); // sb.append(seq.getEndOffset()); // sb.append(", {"); // // Enumeration attrNames = seq.getAttributes().getAttributeNames(); // while(attrNames.hasMoreElements()) { // Object attrName = attrNames.nextElement(); // Object attrValue = seq.getAttributes().getAttribute(attrName); // // sb.append(attrName == null ? "null" : attrName.toString()); // sb.append(" = "); // sb.append(attrValue == null ? "null" : attrValue.toString()); // // if (attrNames.hasMoreElements()) { // sb.append(", "); // } // } // // sb.append("}>"); // LOG.log(Level.FINE, sb.toString()); // } // LOG.log(Level.FINE, "--- End of Dumping highlights"); } setNextActivityChangeOffset(0); JTextComponent pane = paneRef.get(); if (pane != null) { int rangeEnd = Math.min(event.getEndOffset(), pane.getDocument().getLength()); int rangeStart = event.getStartOffset() >= rangeEnd ? 0 : event.getStartOffset(); if (rangeStart < rangeEnd) { try { pane.getUI().damageRange(pane, rangeStart, rangeEnd); } catch (Exception e) { LOG.log(Level.INFO, "Can't update view: range = [" + rangeStart + ", " + rangeEnd + "]", e); //NOI18N } } } } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private boolean processOffset(DrawContext ctx, boolean applyAttributes) { int currentOffset = ctx.getFragmentOffset(); int endOffset = currentOffset + 64; Document doc = ctx.getEditorUI().getDocument(); if (endOffset >= doc.getLength()) { endOffset = Integer.MAX_VALUE; } HighlightsSequence hs = highlights.getHighlights(currentOffset, endOffset); boolean hasHighlight = hs.moveNext(); if (hasHighlight) { if (hs.getStartOffset() <= currentOffset) { if (applyAttributes) { Coloring coloring = Coloring.fromAttributeSet(hs.getAttributes()); coloring.apply(ctx); } lastAttributeSet = hs.getAttributes(); setNextActivityChangeOffset(hs.getEndOffset()); } else { setNextActivityChangeOffset(hs.getStartOffset()); } return true; } else { return false; } } private AttributeSet [] filter(List sets) { ArrayList eolSets = new ArrayList(); ArrayList elSets = new ArrayList(); for(AttributeSet set : sets) { Object value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EOL); if ((value instanceof Boolean) && ((Boolean) value).booleanValue()) { eolSets.add(set); } value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EMPTY_LINE); if ((value instanceof Boolean) && ((Boolean) value).booleanValue()) { elSets.add(set); } } AttributeSet eolAttribs; if (eolSets.size() > 1) { eolAttribs = AttributesUtilities.createComposite(eolSets.toArray(new AttributeSet[eolSets.size()])); } else if (eolSets.size() == 1) { eolAttribs = eolSets.get(0); } else { eolAttribs = SimpleAttributeSet.EMPTY; } AttributeSet elAttribs; if (elSets.size() > 1) { elAttribs = AttributesUtilities.createComposite(elSets.toArray(new AttributeSet[elSets.size()])); } else if (elSets.size() == 1) { elAttribs = elSets.get(0); } else { elAttribs = SimpleAttributeSet.EMPTY; } return new AttributeSet [] { eolAttribs, elAttribs }; } private static final class Injector implements ChangeListener { public Injector() { } public void stateChanged(ChangeEvent e) { final BaseDocument doc = Registry.getMostActiveDocument(); final JTextComponent comp = Registry.getMostActiveComponent(); if (!tryRegistering(doc, comp)) { RequestProcessor.getDefault().post(new Runnable() { public void run() { SwingUtilities.invokeLater(new Runnable() { public void run() { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Retrying to register, doc = " + simpleToString(doc) + ", component = " + simpleToString(comp)); //NOI18N } tryRegistering(doc, comp); } }); } }, 100); } } private boolean tryRegistering(BaseDocument baseDocument, JTextComponent component) { boolean success = true; if (baseDocument != null) { @SuppressWarnings("unchecked") //NOI18N WeakReference ref = (WeakReference) baseDocument.getProperty("documentInstalledInComponentWeakRef"); //NOI18N JTextComponent comp = ref == null ? null : ref.get(); if (comp != null) { success &= assureRegistered(comp); } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("No component on active document = " + simpleToString(baseDocument)); //NOI18N } } } else { LOG.fine("No active document"); //NOI18N } if (component != null) { success &= assureRegistered(component); } else { LOG.fine("No active component"); //NOI18N } return success; } private boolean assureRegistered(JTextComponent comp) { EditorUI eui = Utilities.getEditorUI(comp); if (eui != null) { DrawLayer layerA = eui.findLayer(LAYER_A_NAME); if (layerA == null) { layerA = new HighlightingDrawLayer(LAYER_A_NAME, comp, FILTER_A); eui.addLayer(layerA, 10000); // the old caret row draw layer's z-order if (LOG.isLoggable(Level.FINE)) { LOG.fine("Successfully registered layerA in " + simpleToString(comp)); //NOI18N } } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("LayerA is already registered in " + simpleToString(comp)); //NOI18N } } DrawLayer layerB = eui.findLayer(LAYER_B_NAME); if (layerB == null) { layerB = new HighlightingDrawLayer(LAYER_B_NAME, comp, FILTER_B); eui.addLayer(layerB, 2050); // the old caret row draw layer's z-order if (LOG.isLoggable(Level.FINE)) { LOG.fine("Successfully registered layerB in " + simpleToString(comp)); //NOI18N } } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("LayerB is already registered in " + simpleToString(comp)); //NOI18N } } DrawLayer layerC = eui.findLayer(LAYER_C_NAME); if (layerC == null) { layerC = new HighlightingDrawLayer(LAYER_C_NAME, comp, FILTER_C); eui.addLayer(layerC, 1000); // the old syntax draw layer's z-order if (LOG.isLoggable(Level.FINE)) { LOG.fine("Successfully registered layerC in " + simpleToString(comp)); //NOI18N } } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("LayerC is already registered in " + simpleToString(comp)); //NOI18N } } return true; } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("No EditorUI on component = " + simpleToString(comp)); //NOI18N } return false; } } private String simpleToString(Object o) { return o == null ? "null" : o.getClass() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N } } // End of Injector class } File [added]: Installer.java Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/Installer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 25 --------------- package org.netbeans.modules.editor.oldlibbridge; import java.util.logging.Logger; import org.netbeans.editor.Registry; import org.openide.modules.ModuleInstall; /** * Manages a module's lifecycle. Remember that an installer is optional and * often not needed at all. */ public class Installer extends ModuleInstall { private static final Logger LOG = Logger.getLogger(Installer.class.getName()); // /** The flag that determines if the new highlighting API should be used. */ // private static final boolean HIGHLIGHTING = Boolean.getBoolean("org-netbeans-spi-editor-highlighting"); public void restored() { // LOG.warning("Highlighting is " + (HIGHLIGHTING ? "on" : "off") + "."); // if (HIGHLIGHTING) { Registry.addChangeListener(HighlightingDrawLayer.INJECTOR); // } } } File [added]: NonLexerSyntaxHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/NonLexerSyntaxHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 312 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.oldlibbridge; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.TokenCategory; import org.netbeans.editor.TokenContextPath; import org.netbeans.editor.TokenID; import org.netbeans.editor.TokenItem; import org.netbeans.editor.ext.ExtSyntaxSupport; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.WeakListeners; /** * * @author Vita Stejskal */ public final class NonLexerSyntaxHighlighting extends AbstractHighlightsContainer implements DocumentListener, LookupListener { private static final Logger LOG = Logger.getLogger(NonLexerSyntaxHighlighting.class.getName()); public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.oldlibbridge.NonLexerSyntaxHighlighting"; //NOI18N private final Document document; private long version = 0; private final MimePath mimePath; private final Lookup.Result lookupResult; private final WeakHashMap attribsCache = new WeakHashMap(); /** Creates a new instance of NonLexerSytaxHighlighting */ public NonLexerSyntaxHighlighting(Document document, String mimeType) { this.mimePath = MimePath.parse(mimeType); this.lookupResult = MimeLookup.getLookup(mimePath).lookup(new Lookup.Template(FontColorSettings.class)); this.lookupResult.addLookupListener(this); this.document = document; this.document.addDocumentListener(WeakListeners.document(this, document)); } public HighlightsSequence getHighlights(int startOffset, int endOffset) { synchronized (this) { if (document instanceof BaseDocument) { return new HSImpl(version, (BaseDocument) document, startOffset, endOffset); } else { return HighlightsSequence.EMPTY; } } } // ---------------------------------------------------------------------- // DocumentListener implementation // ---------------------------------------------------------------------- public void insertUpdate(DocumentEvent e) { documentChanged(e.getOffset(), e.getLength()); } public void removeUpdate(DocumentEvent e) { documentChanged(e.getOffset(), e.getLength()); } public void changedUpdate(DocumentEvent e) { documentChanged(e.getOffset(), e.getLength()); } // ---------------------------------------------------------------------- // LookupListener implementation // ---------------------------------------------------------------------- public void resultChanged(LookupEvent ev) { synchronized (this) { attribsCache.clear(); version++; } document.render(new Runnable() { public void run() { fireHighlightsChange(0, Integer.MAX_VALUE); } }); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private AttributeSet findAttribs(TokenItem tokenItem) { synchronized (this) { AttributeSet attribs = attribsCache.get(tokenItem.getTokenID()); if (attribs == null) { Collection allFcs = lookupResult.allInstances(); if (!allFcs.isEmpty()) { FontColorSettings fcs = allFcs.iterator().next(); attribs = findFontAndColors(fcs, tokenItem); if (attribs == null) { attribs = SimpleAttributeSet.EMPTY; } attribsCache.put(tokenItem.getTokenID(), attribs); } else { LOG.warning("Can't find FCS for mime path: '" + mimePath.getPath() + "'"); //NOI18N } } return attribs == null ? SimpleAttributeSet.EMPTY : attribs; } } private static AttributeSet findFontAndColors(FontColorSettings fcs, TokenItem tokenItem) { AttributeSet attribs = null; TokenContextPath tokenContextPath = tokenItem.getTokenContextPath(); // First try the token's name { String name = tokenContextPath.getFullTokenName(tokenItem.getTokenID()); if (name != null) { attribs = fcs.getTokenFontColors(name); } } // Then try the category if (attribs == null) { TokenCategory category = tokenItem.getTokenID().getCategory(); if (category != null) { String categoryName = tokenContextPath.getFullTokenName(category); if (categoryName != null) { attribs = fcs.getTokenFontColors(categoryName); } } } return attribs; } private void documentChanged(int offset, int lenght) { synchronized (this) { version++; } if (LOG.isLoggable(Level.FINE)) { LOG.fine("Document changed: changeStart = " + offset + ", changeEnd = " + (offset + lenght)); //NOI18N } if (offset < 0 || offset > document.getLength()) { offset = 0; } if (lenght <= 0 || offset + lenght > document.getLength()) { lenght = document.getLength() - offset; } fireHighlightsChange(offset, offset + lenght); } private final class HSImpl implements HighlightsSequence { private final long version; private final BaseDocument baseDocument; private final int startOffset; private final int endOffset; private boolean init = false; private TokenItem tokenItem; public HSImpl(long version, BaseDocument baseDocument, int startOffset, int endOffset) { this.version = version; this.baseDocument = baseDocument; this.startOffset = startOffset; this.endOffset = endOffset; } public boolean moveNext() { if (!init) { init = true; try { ExtSyntaxSupport ess = new ExtSyntaxSupport(baseDocument); tokenItem = ess.getTokenChain(startOffset, endOffset); } catch (BadLocationException e) { LOG.log(Level.WARNING, "Can't get token sequence: document " + baseDocument + //NOI18N ", startOffset = " + startOffset + ", endOffset = " + endOffset, e); //NOI18N tokenItem = null; } while(null != tokenItem) { if (tokenItem.getOffset() + tokenItem.getImage().length() > startOffset) { break; } if (LOG.isLoggable(Level.FINE)) { LOG.fine("Skipping tokenId: " + tokenItem.getTokenID() + //NOI18N ", tokenStart = " + tokenItem.getOffset() + //NOI18N ", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + //NOI18N ", startOffset = " + startOffset + //NOI18N ", endOffset = " + endOffset //NOI18N ); } tokenItem = tokenItem.getNext(); } } else if (tokenItem != null) { tokenItem = tokenItem.getNext(); } if (tokenItem != null && tokenItem.getOffset() > endOffset) { tokenItem = null; } if (LOG.isLoggable(Level.FINE)) { if (tokenItem != null) { LOG.fine("Next tokenId: " + tokenItem.getTokenID() + //NOI18N ", tokenStart = " + tokenItem.getOffset() + //NOI18N ", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + //NOI18N ", startOffset = " + startOffset + //NOI18N ", endOffset = " + endOffset //NOI18N ); } else { LOG.fine("Next tokenId: null"); //NOI18N } } return tokenItem != null; } public int getStartOffset() { synchronized (NonLexerSyntaxHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (tokenItem == null) { throw new NoSuchElementException(); } return Math.max(tokenItem.getOffset(), startOffset); } } public int getEndOffset() { synchronized (NonLexerSyntaxHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (tokenItem == null) { throw new NoSuchElementException(); } return Math.min(tokenItem.getOffset() + tokenItem.getImage().length(), endOffset); } } public AttributeSet getAttributes() { synchronized (NonLexerSyntaxHighlighting.this) { checkVersion(); if (!init) { throw new NoSuchElementException("Call moveNext() first."); //NOI18N } else if (tokenItem == null) { throw new NoSuchElementException(); } return findAttribs(tokenItem); } } private void checkVersion() { if (this.version != NonLexerSyntaxHighlighting.this.version) { throw new ConcurrentModificationException(); } } } // End of HSImpl class } File [added]: layer.xml Url: http://editor.netbeans.org/source/browse/editor/lib/bridge/src/org/netbeans/modules/editor/oldlibbridge/layer.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 9 -------------- Directory: /editor/lib/nbproject/ ================================= File [changed]: project.properties Url: http://editor.netbeans.org/source/browse/editor/lib/nbproject/project.properties?r1=1.15&r2=1.16 Delta lines: +1 -1 ------------------- --- project.properties 5 Jan 2007 03:30:37 -0000 1.15 +++ project.properties 19 Jan 2007 05:21:13 -0000 1.16 @@ -17,7 +17,7 @@ javac.compilerargs=-Xlint:unchecked javac.source=1.5 -spec.version.base=1.11.0 +spec.version.base=1.12.0 is.autoload=true src.dir=../libsrc File [changed]: project.xml Url: http://editor.netbeans.org/source/browse/editor/lib/nbproject/project.xml?r1=1.7&r2=1.8 Delta lines: +51 -10 --------------------- --- project.xml 30 Jun 2006 19:17:45 -0000 1.7 +++ project.xml 19 Jan 2007 05:21:13 -0000 1.8 @@ -32,27 +32,38 @@ - org.netbeans.modules.editor.util + org.netbeans.modules.editor.lib2 1 + - org.openide.util + org.netbeans.modules.editor.mimelookup - 6.2 + 1 + 1.6 - org.openide.dialogs + org.netbeans.modules.editor.settings + + + + 1 + 1.5 + + + + org.netbeans.modules.editor.util - 6.2 + 1 @@ -63,17 +74,47 @@ 6.5 + + org.openide.dialogs + + + + 7.1 + + + + org.openide.util + + + + 7.3 + + + + + unit + + org.netbeans.modules.editor.lib + + + + + org.netbeans.modules.lexer + + + + + + qa-functional + + org.netbeans.editor org.netbeans.editor.ext - org.netbeans.editor.ext.html - org.netbeans.editor.ext.html.dtd - org.netbeans.editor.ext.java - org.netbeans.editor.ext.plain org.netbeans.editor.view.spi - org.netbeans.lib.editor.view org.netbeans.lib.editor.hyperlink.spi + org.netbeans.lib.editor.view Directory: /editor/lib/test/ ============================ File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib/test/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 3 -------------- lib results work File [added]: build.xml Url: http://editor.netbeans.org/source/browse/editor/lib/test/build.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 32 --------------- File [added]: cfg-unit.xml Url: http://editor.netbeans.org/source/browse/editor/lib/test/cfg-unit.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 60 --------------- Directory: /editor/lib2/ ======================== File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib2/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- build File [added]: apichanges.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/apichanges.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 123 ---------------- General editor/lib2 was created. The module was created. Change History for the Editor Library 2 API

Introduction

This document lists changes made to the Editor Library 2 API.



@FOOTER@

File [added]: arch.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/arch.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 933 ---------------- ]> > &api-questions; The Editor Library 2 module is a set of official APIs and SPIs.
It replaces the original Editor Library with legacy APIs that were not properly structured and did not conform to the rules implied on the current NB APIs.
The editor functionality are mainly implementations of the Swing Text package APIs plus extension features such as Syntax Coloring or Word Matching.
Usecases of the Highlighting SPI can be found in its package overview.
Other usecases - TBD.
Code is accompanied with unit tests. Most of the work should be ready in NetBeans 6.0. Yes. It conforms to the Swing Text Package API. Yes. Needs at least JRE 1.5. JRE is sufficient. No non-NetBeans projects. The module is 100% pure Java and runs on any platform.
OpenIDE-Module-Module-Dependencies: org.netbeans.modules.editor.lib2/1 > @SPECIFICATION-VERSION@
No additional files. Yes. Yes, where appropriate. Module can be installed anywhere. No. No. No. No. No.
  • Editor just forwards the clipboard operation requests into javax.swing.text.JTextComponent's cut(), copy() and paste() methods.
None. No files read/written to disk (file reading into editor document not handled here). No. No. No. No. No. No explicit limits. Technically, the available memory size is the limit... One average source file opened in the editor occupies roughly 500KB. No. Opening a file in the editor can be a long task for large files. Number of characters of the source file multiplied by 2 to respect 2 bytes for each unicode characters.
Each line separator adds another line element (about 96 bytes) plus a position object (48 bytes).
No. No. No. No. No. No. No. No.

Threading model of the EditorDocument adheres to javax.swing.text.Document interface. There can be multiple reader threads accessing the document simultaneously but only one mutating thread at the time.

All the UI-related tasks adhere to Swing/AWT conventions i.e. they must be performed in EQ thread.

The listeners attached to text document should execute quickly. For copmlex tasks they should reschedule their work into another thread. No. No. No. The APIs and SPIs in editor/lib2 module should become offical replacements for the legacy editor APIs that do not conform with the Netbeans API standards. No.
File [added]: build.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/build.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 22 --------------- File [added]: manifest.mf Url: http://editor.netbeans.org/source/browse/editor/lib2/manifest.mf?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 5 -------------- Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor.lib2/1 OpenIDE-Module-Implementation-Version: 2 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml Directory: /editor/lib2/nbproject/ ================================== File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib2/nbproject/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- private File [added]: project.properties Url: http://editor.netbeans.org/source/browse/editor/lib2/nbproject/project.properties?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 25 --------------- # The contents of this file are subject to the terms of the Common Development # and Distribution License (the License). You may not use this file except in # compliance with the License. # # You can obtain a copy of the License at http://www.netbeans.org/cddl.html # or http://www.netbeans.org/cddl.txt. # # When distributing Covered Code, include this CDDL Header Notice in each file # and include the License file at http://www.netbeans.org/cddl.txt. # If applicable, add the following below the CDDL Header, with the fields # enclosed by brackets [] replaced by your own identifying information: # "Portions Copyrighted [year] [name of copyright owner]" # # The Original Software is NetBeans. The Initial Developer of the Original # Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun # Microsystems, Inc. All Rights Reserved. is.autoload=true javac.source=1.5 javac.compilerargs=-Xlint:unchecked spec.version.base=1.2 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml javadoc.title=Editor Library 2 API File [added]: project.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/nbproject/project.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 119 ---------------- org.netbeans.modules.apisupport.project org.netbeans.modules.editor.lib2 org.netbeans.modules.editor.mimelookup 1 1.5 org.netbeans.modules.editor.settings 1 1.5 org.netbeans.modules.editor.util 1 1.12 org.netbeans.modules.lexer 2 1.7 org.openide.dialogs 7.1 org.openide.util 6.2 unit org.netbeans.modules.editor.lib2 org.netbeans.modules.editor.util org.netbeans.modules.lexer org.netbeans.modules.java.lexer org.netbeans.modules.editor.settings.storage org.netbeans.modules.editor.mimelookup.impl qa-functional org.netbeans.spi.editor.highlighting org.netbeans.spi.editor.highlighting.support Directory: /editor/lib2/src/org/netbeans/modules/editor/lib2/ ============================================================= File [added]: Acceptor.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/Acceptor.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 36 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; /** Accept or reject given character. The advance is that accepting can * be done by code so the methods from java.lang.Character can be used * and mixed. * * @author Jaroslav Tulach * @version 1.00 */ public interface Acceptor { /** Accept or reject character * @return true to accept the given character or false to not accept it */ public boolean accept(char ch); } File [added]: AcceptorFactory.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/AcceptorFactory.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 101 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; /** Mostly used acceptors * * @author Miloslav Metelka * @version 1.00 */ public class AcceptorFactory { public static final Acceptor TRUE = new Fixed(true); public static final Acceptor FALSE = new Fixed(false); public static final Acceptor NL = new Char('\n'); public static final Acceptor SPACE_NL = new TwoChar(' ', '\n'); public static final Acceptor WHITESPACE = new Acceptor() { public final boolean accept(char ch) { return Character.isWhitespace(ch); } }; public static final Acceptor LETTER_DIGIT = new Acceptor() { public final boolean accept(char ch) { return Character.isLetterOrDigit(ch); } }; public static final Acceptor JAVA_IDENTIFIER = new Acceptor() { public final boolean accept(char ch) { return Character.isJavaIdentifierPart(ch); } }; public static final Acceptor NON_JAVA_IDENTIFIER = new Acceptor() { public final boolean accept(char ch) { return !Character.isJavaIdentifierPart(ch); } }; private static final class Fixed implements Acceptor { private boolean state; public Fixed(boolean state) { this.state = state; } public final boolean accept(char ch) { return state; } } private static final class Char implements Acceptor { private char hit; public Char(char hit) { this.hit = hit; } public final boolean accept(char ch) { return ch == hit; } } private static final class TwoChar implements Acceptor { private char hit1, hit2; public TwoChar(char hit1, char hit2) { this.hit1 = hit1; this.hit2 = hit2; } public final boolean accept(char ch) { return ch == hit1 || ch == hit2; } } } File [added]: Bundle.properties Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/Bundle.properties?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 24 --------------- # The contents of this file are subject to the terms of the Common Development # and Distribution License (the License). You may not use this file except in # compliance with the License. # # You can obtain a copy of the License at http://www.netbeans.org/cddl.html # or http://www.netbeans.org/cddl.txt. # # When distributing Covered Code, include this CDDL Header Notice in each file # and include the License file at http://www.netbeans.org/cddl.txt. # If applicable, add the following below the CDDL Header, with the fields # enclosed by brackets [] replaced by your own identifying information: # "Portions Copyrighted [year] [name of copyright owner]" # # The Original Software is NetBeans. The Initial Developer of the Original # Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun # Microsystems, Inc. All Rights Reserved. OpenIDE-Module-Name=Editor Library 2 OpenIDE-Module-Display-Category=Editing OpenIDE-Module-Short-Description=Contains core editor APIs and SPIs. OpenIDE-Module-Long-Description=The library includes implementations of the text document, view hierarchy and highlighting. The library is independent on the NetBeans IDE. #DocUtils.debugPosition wrong_position=Wrong position File [added]: ComponentUtils.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/ComponentUtils.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 153 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.awt.Component; import java.awt.Frame; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.plaf.TextUI; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; /** * * @author Vita Stejskal */ public final class ComponentUtils { private static final Logger LOG = Logger.getLogger(Logger.class.getName()); public static boolean isGuardedException(BadLocationException exc) { return exc.getClass().getName().equals("GuardedException"); } public static void returnFocus() { JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { requestFocus(c); } } public static void requestFocus(JTextComponent c) { if (c != null) { if (!EditorImplementation.getDefault().activateComponent(c)) { Frame f = getParentFrame(c); if (f != null) { f.requestFocus(); } c.requestFocus(); } } } public static void setStatusText(JTextComponent c, String text) { // TODO: fix this, do not use reflection try { Object editorUI = getEditorUI(c); Method getSbMethod = editorUI.getClass().getMethod("getStatusBar"); Object statusBar = getSbMethod.invoke(editorUI); Method setTextMethod = statusBar.getClass().getMethod("setText", String.class, String.class); setTextMethod.invoke(statusBar, "main", text); } catch (Exception e) { LOG.log(Level.WARNING, e.getMessage(), e); } // StatusBar sb = getEditorUI(c).getStatusBar(); // if (sb != null) { // sb.setText(StatusBar.CELL_MAIN, text); // } } // public static void setStatusText(JTextComponent c, String text, Coloring extraColoring) { // TextUI textUI = c.getUI(); // try { // Method getSbMethod = textUI.getClass().getMethod("getStatusBar"); // Object statusBar = getSbMethod.invoke(textUI); // Method setTextMethod = statusBar.getClass().getMethod("setText", String.class, String.class); // setTextMethod.invoke(statusBar, "main", text); // } catch (Exception e) { // LOG.log(Level.WARNING, e.getMessage(), e); // } //// StatusBar sb = getEditorUI(c).getStatusBar(); //// if (sb != null) { //// sb.setText(StatusBar.CELL_MAIN, text, extraColoring); //// } // } public static void setStatusBoldText(JTextComponent c, String text) { // TODO: fix this, do not use reflection try { Object editorUI = getEditorUI(c); Method getSbMethod = editorUI.getClass().getMethod("getStatusBar"); //NOI18N Object statusBar = getSbMethod.invoke(editorUI); Method setTextMethod = statusBar.getClass().getMethod("setBoldText", String.class, String.class); //NOI18N setTextMethod.invoke(statusBar, "main", text); //NOI18N } catch (Exception e) { LOG.log(Level.WARNING, e.getMessage(), e); } // StatusBar sb = getEditorUI(c).getStatusBar(); // if (sb != null) { // sb.setBoldText(StatusBar.CELL_MAIN, text); // } } public static String getStatusText(JTextComponent c) { // TODO: fix this, do not use reflection try { Object editorUI = getEditorUI(c); Method getSbMethod = editorUI.getClass().getMethod("getStatusBar"); //NOI18N Object statusBar = getSbMethod.invoke(editorUI); Method getTextMethod = statusBar.getClass().getMethod("getText", String.class); //NOI18N return (String) getTextMethod.invoke(statusBar, "main"); //NOI18N } catch (Exception e) { LOG.log(Level.WARNING, e.getMessage(), e); return ""; //NOI18N } // StatusBar sb = getEditorUI(c).getStatusBar(); // return (sb != null) ? sb.getText(StatusBar.CELL_MAIN) : null; } public static void clearStatusText(JTextComponent c) { setStatusText(c, ""); // NOI18N } private static Object getEditorUI(JTextComponent c) throws Exception { // TODO: fix this, do not use reflection TextUI textUI = c.getUI(); Method getEuiMethod = textUI.getClass().getMethod("getEditorUI"); //NOI18N return getEuiMethod.invoke(textUI); } private static Frame getParentFrame(Component c) { do { c = c.getParent(); if (c instanceof Frame) { return (Frame)c; } } while (c != null); return null; } /** Creates a new instance of DocUtils */ private ComponentUtils() { } } File [added]: DialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/DialogSupport.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 196 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import com.sun.org.apache.bcel.internal.generic.LOOKUPSWITCH; import java.awt.Insets; import java.awt.Dialog; import java.awt.event.*; import java.awt.LayoutManager; import java.awt.BorderLayout; import java.awt.GridLayout; import java.util.Collection; import javax.swing.*; import javax.swing.border.EmptyBorder; import org.netbeans.spi.editor.DialogFactory; import org.openide.util.Lookup; /** * DialogSupport is factory based class for creating dialogs of certain * behaviour. It is intended to be used whenever editor needs to popup a dialog. * It presents a way for changing the implementation of the dialog depending * on the enviroment the Editor is embeded in. * * @author pnejedly * @version 1.0 */ public final class DialogSupport { private static DialogSupport instance; private DialogFactory externalFactory; private Lookup.Result result; public static synchronized DialogSupport getInstance() { if (instance == null) { instance = new DialogSupport(); } return instance; } /** Noone needs to instantiate the dialog support */ private DialogSupport() { result = Lookup.getDefault().lookup(new Lookup.Template(DialogFactory.class)); } /** * The method for creating a dialog with specified properties. * @param title The title of created dialog. * @param panel The content of the dialog to be displayed. * @param modal Whether the dialog should be modal. * @param buttons The array of JButtons to be added to the dialog. * @param sidebuttons The buttons could be placed under the panel (false), * or on the right side of the panel (true). * @param defaultIndex The index of default button in the buttons array, * if index < 0, no default button is set. * @param cancelIndex The index about cancel button - the button that will * be pressed when closing the dialog. * @param listener The listener which will be notified of all button * events. * @return newly created Dialog */ public Dialog createDialog( String title, JPanel panel, boolean modal, JButton[] buttons, boolean sidebuttons, int defaultIndex, int cancelIndex, ActionListener listener ) { DialogFactory factory = null; if( externalFactory != null ) { factory = externalFactory; } else { Collection factories = result.allInstances(); if (factories.isEmpty()) { factory = new DefaultDialogFactory(); } else { factory = factories.iterator().next(); } } return factory.createDialog(title, panel, modal, buttons, sidebuttons, defaultIndex, cancelIndex, listener ); } /** The method for setting custom factory for creating dialogs via * the {@link #createDialog(java.lang.String, javax.swing.JPanel, boolean, javax.swing.JButton[], boolean, int, int, java.awt.event.ActionListener) createDialog} method. * If no factory is set, the {@link DialogSupport.DefaultDialogFactory DefaultDialogFactory} * will be used. * *

IMPORTANT: This method is here only for supporting the backwards * compatibility of the {@link org.netbeans.editor.DialogSupport} class. * * @param factory the {@link DialogSupport.DialogFactory DialogFactory} * implementation that will be responsible for providing dialogs. */ public void setExternalDialogFactory( DialogFactory factory ) { externalFactory = factory; } /** The DialogFactory that will be used to create Dialogs if no other * DialogFactory is set to DialogSupport. */ private static class DefaultDialogFactory extends WindowAdapter implements DialogFactory, ActionListener { private JButton cancelButton; /** Create a panel with buttons that will be placed according * to the required alignment */ private JPanel createButtonPanel( JButton[] buttons, boolean sidebuttons ) { int count = buttons.length; JPanel outerPanel = new JPanel( new BorderLayout() ); outerPanel.setBorder( new EmptyBorder( new Insets( sidebuttons ? 5 : 0, sidebuttons ? 0 : 5, 5, 5 ) ) ); LayoutManager lm = new GridLayout( // GridLayout makes equal cells sidebuttons ? count : 1, sidebuttons ? 1 : count, 5, 5 ); JPanel innerPanel = new JPanel( lm ); for( int i = 0; i < count; i++ ) innerPanel.add( buttons[i] ); outerPanel.add( innerPanel, sidebuttons ? BorderLayout.NORTH : BorderLayout.EAST ) ; return outerPanel; } public Dialog createDialog( String title, JPanel panel, boolean modal, JButton[] buttons, boolean sidebuttons, int defaultIndex, int cancelIndex, ActionListener listener ) { // create the dialog with given content JDialog d = new JDialog( (javax.swing.JFrame)null, title, modal ); d.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); d.getContentPane().add( panel, BorderLayout.CENTER); // Add the buttons to it JPanel buttonPanel = createButtonPanel( buttons, sidebuttons ); String buttonAlign = sidebuttons ? BorderLayout.EAST : BorderLayout.SOUTH; d.getContentPane().add( buttonPanel, buttonAlign ); // add listener to buttons if( listener != null ) { for( int i = 0; i < buttons.length; i++ ) { buttons[i].addActionListener( listener ); } } // register the default button, if available if( defaultIndex >= 0 ) { d.getRootPane().setDefaultButton( buttons[defaultIndex] ); } // register the cancel button helpers, if available if( cancelIndex >= 0 ) { cancelButton = buttons[cancelIndex]; // redirect the Esc key to Cancel button d.getRootPane().registerKeyboardAction( this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), JComponent.WHEN_IN_FOCUSED_WINDOW ); // listen on windowClosing and redirect it to Cancel button d.addWindowListener( this ); } d.pack(); return d; } public void actionPerformed(ActionEvent evt) { cancelButton.doClick( 10 ); } public void windowClosing( WindowEvent evt ) { cancelButton.doClick( 10 ); } } // End of DefaultDialogFactory class } File [added]: DocUtils.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/DocUtils.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 166 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import org.openide.util.NbBundle; /** * This class contains useful methods for working with documents. * * @author Vita Stejskal */ public final class DocUtils { private static final Logger LOG = Logger.getLogger(DocUtils.class.getName()); public static int getRowStart(Document doc, int offset, int lineShift) throws BadLocationException { checkOffsetValid(doc, offset); if (lineShift != 0) { Element lineRoot = doc.getDefaultRootElement(); int line = lineRoot.getElementIndex(offset); line += lineShift; if (line < 0 || line >= lineRoot.getElementCount()) { return -1; // invalid line shift } return lineRoot.getElement(line).getStartOffset(); } else { // no shift return doc.getDefaultRootElement().getElement( doc.getDefaultRootElement().getElementIndex(offset)).getStartOffset(); } } public static int getRowEnd(Document doc, int offset) throws BadLocationException { checkOffsetValid(doc, offset); return doc.getDefaultRootElement().getElement( doc.getDefaultRootElement().getElementIndex(offset)).getEndOffset() - 1; } /** * Return line offset (line number - 1) for some position in the document. * * @param doc document to operate on * @param offset position in document where to start searching */ public static int getLineOffset(Document doc, int offset) throws BadLocationException { checkOffsetValid(offset, doc.getLength() + 1); Element lineRoot = doc.getDefaultRootElement(); return lineRoot.getElementIndex(offset); } public static String debugPosition(Document doc, int offset) { String ret; if (offset >= 0) { try { int line = getLineOffset(doc, offset) + 1; int col = getVisualColumn(doc, offset) + 1; ret = String.valueOf(line) + ":" + String.valueOf(col); // NOI18N } catch (BadLocationException e) { ret = NbBundle.getBundle(DocUtils.class).getString("wrong_position") + ' ' + offset + " > " + doc.getLength(); // NOI18N } } else { ret = String.valueOf(offset); } return ret; } /** Return visual column (with expanded tabs) on the line. * @param doc document to operate on * @param offset position in document for which the visual column should be found * @return visual column on the line determined by position */ public static int getVisualColumn(Document doc, int offset) throws BadLocationException { int docLen = doc.getLength(); if (offset == docLen + 1) { // at ending extra '\n' => make docLen to proceed without BLE offset = docLen; } // TODO: fix this, do not use reflection try { Method m = doc.getClass().getMethod("getVisColFromPos", Integer.TYPE); return (Integer) m.invoke(doc, offset); // return doc.getVisColFromPos(offset); } catch (Exception e) { return -1; } } public static boolean isIdentifierPart(Document doc, char ch) { // TODO: make this configurable return AcceptorFactory.LETTER_DIGIT.accept(ch); } public static boolean isWhitespace(char ch) { // TODO: make this configurable return AcceptorFactory.WHITESPACE.accept(ch); } public static void atomicLock(Document doc) { // TODO: fix this, do not use reflection try { Method lockMethod = doc.getClass().getMethod("atomicLock"); lockMethod.invoke(doc); } catch (Exception e) { LOG.log(Level.WARNING, e.getMessage(), e); } } public static void atomicUnlock(Document doc) { // TODO: fix this, do not use reflection try { Method unlockMethod = doc.getClass().getMethod("atomicUnlock"); unlockMethod.invoke(doc); } catch (Exception e) { LOG.log(Level.WARNING, e.getMessage(), e); } } private static void checkOffsetValid(Document doc, int offset) throws BadLocationException { checkOffsetValid(offset, doc.getLength()); } private static void checkOffsetValid(int offset, int limitOffset) throws BadLocationException { if (offset < 0 || offset > limitOffset) { throw new BadLocationException("Invalid offset=" + offset // NOI18N + " not within <0, " + limitOffset + ">", // NOI18N offset); } } /** Creates a new instance of DocUtils */ private DocUtils() { } } File [added]: DocumentsJumpList.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/DocumentsJumpList.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 386 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.BadLocationException; import javax.swing.text.Position; import org.openide.util.Utilities; /** * The last several jumps in either the current file * or across several files is stored here in the list. * It's possible to track this list. * * @author Miloslav Metelka * @version 1.00 */ public class DocumentsJumpList { /** Maximum size to which the list will be shrinked * if it exceeds the THRESHOLD_SIZE. */ private static final int MAX_SIZE = 50; /** Reaching this count means that the size should be checked * and possibly shrinked to the MAX_SIZE. */ private static final int CHECK_COUNT = 10; /** Current jump list entry */ private static Entry currentEntry; private static int checkCnt; private static final PropertyChangeSupport support = new PropertyChangeSupport(DocumentsJumpList.class);; private static boolean dotAtCurrentEntry = false; private static ChangeListener registryListener = new ChangeListener() { public void stateChanged(ChangeEvent e) { support.firePropertyChange(null, null, null); } }; static { DocumentsRegistry.addChangeListener(registryListener); } public static void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener(listener); } public static void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener(listener); } public static void dotMoved(JTextComponent c, int offset) { if (dotAtCurrentEntry && currentEntry != null && (currentEntry.getComponent() != c || currentEntry.getPosition() != offset)) { support.firePropertyChange(null, null, null); dotAtCurrentEntry = false; } else { dotAtCurrentEntry = currentEntry != null && currentEntry.getComponent() == c && currentEntry.getPosition() == offset; } } public static void checkAddEntry() { JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { checkAddEntry(c, c.getCaret().getDot()); } } public static void checkAddEntry(JTextComponent c) { checkAddEntry(c, c.getCaret().getDot()); } public static void checkAddEntry(JTextComponent c, int pos) { if (currentEntry == null || currentEntry.getComponent() != c || currentEntry.getPosition() != pos ) { addEntry(c, pos); } } public static void addEntry(JTextComponent c, int pos) { try { Entry e = new Entry(c, pos, currentEntry); currentEntry = e; if (++checkCnt >= CHECK_COUNT) { // perform size check sizeCheck(); } dotAtCurrentEntry = true; } catch (BadLocationException e) { // entry not added } } /** * @param c current component. It's used to compare the current * component and position with those stored in the entries. */ public static void jumpPrev(JTextComponent c) { int dotPos = c.getCaret().getDot(); if (currentEntry != null) { while (true) { int entryPos = currentEntry.getPosition(); JTextComponent entryComp = currentEntry.getComponent(); if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { if (currentEntry.setDot()) { support.firePropertyChange(null, null, null); break; } } if (currentEntry.prev != null) { // must check not to end up with null currentEntry = currentEntry.prev; } else { break; // break when on the last entry } } } } public static boolean hasPrev() { JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { int dotPos = c.getCaret().getDot(); Entry e = currentEntry; if (e != null) { while (true) { int entryPos = e.getPosition(); JTextComponent entryComp = e.getComponent(); if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { if (entryPos >= 0 && entryPos <= entryComp.getDocument().getLength()) { return true; } } if (e.prev != null) { // must check not to end up with null e = e.prev; } else { break; // break when on the last entry } } } } return false; } /** * @param c current component. It's used to compare the current * component and position with those stored in the entries. */ public static void jumpNext(JTextComponent c) { int dotPos = c.getCaret().getDot(); if (currentEntry != null) currentEntry = currentEntry.next; if (currentEntry != null) { while (true) { int entryPos = currentEntry.getPosition(); JTextComponent entryComp = currentEntry.getComponent(); if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { if (currentEntry.setDot()) { support.firePropertyChange(null, null, null); break; } } if (currentEntry.next != null) { // must check not to end up with null currentEntry = currentEntry.next; } else { break; // break when on the last entry } } } } public static boolean hasNext() { JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { int dotPos = c.getCaret().getDot(); Entry e = currentEntry != null ? currentEntry.next : currentEntry; if (e != null) { while (true) { int entryPos = e.getPosition(); JTextComponent entryComp = e.getComponent(); if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { if (entryPos >= 0 && entryPos <= entryComp.getDocument().getLength()) { return true; } } if (e.next!= null) { // must check not to end up with null e = e.next; } else { break; // break when on the last entry } } } } return false; } /** * @param c current component. It's used to compare the current * component to those stored in the jump list entries. */ public static void jumpPrevComponent(JTextComponent c) { if (currentEntry != null) { while (true) { JTextComponent entryComp = currentEntry.getComponent(); if (entryComp != null && entryComp != c) { if (currentEntry.setDot()) { break; } } if (currentEntry.prev != null) { // must check not to end up with null currentEntry = currentEntry.prev; } else { break; // break when on the last entry } } } } /** * @param c current component. It's used to compare the current * component to those stored in the jump list entries. */ public static void jumpNextComponent(JTextComponent c) { if (currentEntry != null) { while (true) { JTextComponent entryComp = currentEntry.getComponent(); if (entryComp != null && entryComp != c) { if (currentEntry.setDot()) { break; } } if (currentEntry.next != null) { // must check not to end up with null currentEntry = currentEntry.next; } else { break; // break when on the last entry } } } } public static String dump() { StringBuffer sb = new StringBuffer(); int i = 0; Entry e = currentEntry; if (e != null) { while (true) { if (e.prev != null) { e = e.prev; i--; } else { break; } } while (e != null) { JTextComponent comp = e.getComponent(); String docStr = (comp != null) ? (String)comp.getDocument().getProperty(Document.TitleProperty) : ""; // NOI18N if (docStr == null) { // no title property docStr = "Untitled"; // NOI18N } sb.append("[" + i++ + "]=" + docStr + ", " + e.getPosition() + "\n"); // NOI18N e = e.next; } } else { // null current entry sb.append("Empty list"); // NOI18N } return sb.toString(); } private static void sizeCheck() { int cnt = MAX_SIZE; Entry e = currentEntry; while (e != null && cnt > 0) { e = e.prev; cnt--; // #19429 } if (e != null) { // reached the one that should be the first e.makeFirst(); } } public static class Entry { /** ID of the stored position component */ private int componentID; /** The position stored in the document */ private Position pos; /** Previous entry in the linked list */ private Entry prev; /** Next entry in the linked list */ private Entry next; private Entry(JTextComponent component, int offset, Entry last) throws BadLocationException { componentID = DocumentsRegistry.getID(component); // TODO: remove // posID = ((BaseDocument)component.getDocument()).storePosition(offset); pos = component.getDocument().createPosition(offset); if (last != null) { // apend after the last entry last.next = this; this.prev = last; } } public int getPosition() { return pos.getOffset(); } public JTextComponent getComponent() { return DocumentsRegistry.getComponent(componentID); } /** Set the dot to the component and position * stored in the mark. * @return true if the caret was successfully moved */ public boolean setDot() { JTextComponent c = getComponent(); if (c != null) { if (DocumentsRegistry.getMostActiveComponent() != c) { ComponentUtils.requestFocus(c); // possibly request for the component DocumentsRegistry.activate(c); } int pos = getPosition(); if (pos >= 0 && pos <= c.getDocument().getLength()) { c.getCaret().setDot(pos); // set the dot return true; } } return false; } void makeLast() { if (next != null) { next.prev = null; next = null; } } void makeFirst() { if (prev != null) { prev.next = null; prev = null; } } // TODO: remove // protected void finalize() throws Throwable { // JTextComponent c = DocumentsRegistry.getComponent(componentID); // if (c != null) { // ((BaseDocument)c.getDocument()).removeStoredPosition(posID); // } // super.finalize(); // } } } File [added]: DocumentsRegistry.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/DocumentsRegistry.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 530 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import javax.swing.event.EventListenerList; import javax.swing.text.JTextComponent; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import javax.swing.text.Document; import org.openide.util.WeakListeners; /** * All the documents and components register here so that * they become available to the processing that crosses * different components and documents such as cross document * position stack or word matching. * * @author Miloslav Metelka * @version 1.00 */ public class DocumentsRegistry { /** Identification of a document. */ public static final String DOCUMENT_ID_PROP = "id"; // NOI18N /** Identification of a component. */ public static final String COMPONENT_ID_PROP = "componentId"; // NOI18N private static final WeakReference[] EMPTY = new WeakReference[0]; /** Array of weak references to documents */ private static WeakReference[] docRefs = EMPTY; /** Number of the document references */ private static int docRefsCount; /** Array of activated document numbers */ private static final ArrayList docAct = new ArrayList(); /** Array list of weak references to components */ private static WeakReference[] compRefs = EMPTY; /** Number of the document references */ private static int compRefsCount; /** Array of activated component numbers */ private static final ArrayList compAct = new ArrayList(); /** List of the registered changes listeners */ private static final EventListenerList listenerList = new EventListenerList(); private static int consolidateCounter; /** Add a listener to listen for changes in the registry. * * @param l listener to add */ public static void addChangeListener(ChangeListener l) { listenerList.add(ChangeListener.class, l); } /** Remove a listener. * * @param l listener to remove */ public static void removeChangeListener(ChangeListener l) { listenerList.remove(ChangeListener.class, l); } /** Get document ID from the document. * @return document id or -1 if document was not yet added to the registry * by addDocument(). */ public static synchronized int getID(Document doc) { Integer i = getIDInteger(doc); return (i != null) ? i.intValue() : -1; } /** Get component ID from the component. * @return component id or -1 if component was not yet added to the registry * by addComponent(). */ public static synchronized int getID(JTextComponent c) { return getIDImpl(c); } /** Get document when its ID is known. * It's rather cheap operation. * @param docID document ID. It can be retrieved from the document * by getID(doc). * @return document instance or null when document no longer exists */ public static synchronized Document getDocument(int docID) { if (docID < 0 || docID >= docRefsCount) { return null; } WeakReference wr = docRefs[docID]; return (wr != null) ? (Document)wr.get() : null; } /** Get component when its ID is known. * It's rather cheap operation. * @param compID component ID. It can be retrieved from the component * by getID(c). * @return component instance or null when document no longer exists */ public static synchronized JTextComponent getComponent(int compID) { if (compID < 0 || compID >= compRefsCount) { return null; } WeakReference wr = compRefs[compID]; return (wr != null) ? (JTextComponent)wr.get() : null; } /** Add document to registry. Doesn't search for repetitive * adding. * @return registry unique ID of the document */ public static synchronized int addDocument(Document doc) { Integer docID = getIDInteger(doc); if (docID != null) { // already added return docID.intValue(); } if (docRefsCount >= docRefs.length) { docRefs = realloc(docRefs); } docRefs[docRefsCount] = new WeakReference(doc); doc.putProperty(DOCUMENT_ID_PROP, new Integer(docRefsCount)); return docRefsCount++; } /** Add component to registry. If the component is already registered * it returns the existing} ID. The document that is currently assigned * to the component is _not_ registered automatically. * @return ID of the component */ public static synchronized int addComponent(JTextComponent c) { int compID = getIDImpl(c); if (compID != -1) { return compID; // already registered } if (compRefsCount >= compRefs.length) { compRefs = realloc(compRefs); } compRefs[compRefsCount] = new WeakReference(c); c.putClientProperty(COMPONENT_ID_PROP, new Integer(compRefsCount)); return compRefsCount++; } /** Remove component from registry. It's usually done when * the UI of the component is being deinstalled. * @return ID that the component had in the registry. The possible * new ID will be different from this one. -1 will be returned * if the component was not yet added to the registry. */ public static synchronized int removeComponent(JTextComponent c) { int compID = getIDImpl(c); if (compID != -1) { compRefs[compID] = null; // Search whether was activated for (int i = compAct.size() - 1; i >= 0; i--) { if (compAct.get(i) == compID) { compAct.remove(i); break; } } } return compID; } /** Put the component to the first position in the array of last accessed * components. The activate of document is also called automatically. */ public static void activate(JTextComponent c) { boolean activated = true; synchronized (DocumentsRegistry.class) { int compID = getIDImpl(c); if (compID == -1) { // c not registered return; } int actSize = compAct.size(); int ind = 0; while (ind < actSize) { int id = compAct.get(ind); if (id == compID) { // found if (ind == 0) { break; } compAct.add(0, compAct.remove(ind)); activated = true; break; } ind++; } if (ind == actSize) { compAct.add(0, compID); activated = true; } // Try to activate component's document too if (doActivate(c.getDocument())) { activated = true; } } if (activated) { fireChange(); } } /** Put the document to the first position in the array of last accessed * documents. The document must be registered otherwise nothing * is done. * @param doc document to be activated */ public static void activate(Document doc) { boolean activated; synchronized (DocumentsRegistry.class) { activated = doActivate(doc); } if (activated) { fireChange(); } } public static synchronized Document getMostActiveDocument() { return getValidDoc(0, true); } public static synchronized Document getLeastActiveDocument() { int lastInd = docAct.size() - 1; return getValidDoc(lastInd, false); } public static Document getLessActiveDocument(Document doc) { return getLessActiveDocument(getID(doc)); } public static synchronized Document getLessActiveDocument(int docID) { return getNextActiveDoc(docID, true); } public static Document getMoreActiveDocument(Document doc) { return getMoreActiveDocument(getID(doc)); } public static synchronized Document getMoreActiveDocument(int docID) { return getNextActiveDoc(docID, false); } /** Get the iterator over the active documents. It starts with * the most active document till the least active document. * It's just the current snapshot so the iterator will * not reflect future changes. */ public static synchronized Iterator getDocumentIterator() { consolidate(); ArrayList docList = new ArrayList(); int actSize = docAct.size(); for (int i = 0; i < actSize; i++) { int ind = docAct.get(i); WeakReference wr = docRefs[ind]; if (wr != null) { Object doc = wr.get(); if (doc != null) { docList.add((Document)doc); } } } return docList.iterator(); } public static synchronized JTextComponent getMostActiveComponent() { return getValidComp(0, true); } public static synchronized JTextComponent getLeastActiveComponent() { int lastInd = compAct.size() - 1; return getValidComp(lastInd, false); } public static JTextComponent getLessActiveComponent(JTextComponent c) { return getLessActiveComponent(getID(c)); } public static synchronized JTextComponent getLessActiveComponent(int compID) { return getNextActiveComp(compID, true); } public static JTextComponent getMoreActiveComponent(JTextComponent c) { return getMoreActiveComponent(getID(c)); } public static synchronized JTextComponent getMoreActiveComponent(int compID) { return getNextActiveComp(compID, false); } /** Get the iterator over the active components. It starts with * the most active component till the least active component. */ public static synchronized Iterator getComponentIterator() { consolidate(); ArrayList compList = new ArrayList(); int actSize = compAct.size(); for (int i = 0; i < actSize; i++) { int ind = compAct.get(i); WeakReference wr = compRefs[ind]; if (wr != null) { Object comp = wr.get(); if (comp != null) { compList.add((JTextComponent) comp); } } } return compList.iterator(); } private static WeakReference[] realloc(WeakReference[] refs) { WeakReference[] tmp = new WeakReference[refs.length * 2 + 4]; System.arraycopy(refs, 0, tmp, 0, refs.length); return tmp; } private static void consolidate() { while (++consolidateCounter >= 20) { // after every 20th call consolidateCounter = 0; // Remove empty document references for (int i = docAct.size() - 1; i >= 0; i--) { int ind = docAct.get(i); WeakReference wr = docRefs[ind]; if (wr != null) { if (wr.get() == null) { // empty reference docAct.remove(i); docRefs[ind] = null; } } } // Remove empty component references for (int i = compAct.size() - 1; i >= 0; i--) { int ind = compAct.get(i); WeakReference wr = compRefs[ind]; if (wr != null) { if (wr.get() == null) { // empty reference compAct.remove(i); compRefs[ind] = null; } } } } } private static int getIDImpl(JTextComponent c) { Integer id = c == null ? null : (Integer)c.getClientProperty(COMPONENT_ID_PROP); return id == null ? -1 : id.intValue(); } private static Integer getIDInteger(Document doc) { if (doc == null) { return null; } return (Integer)doc.getProperty(DOCUMENT_ID_PROP); } private static boolean doActivate(Document doc) { Integer docIDInteger = getIDInteger(doc); if (docIDInteger == null) { return false; // document not added to registry } int docID = (docIDInteger != null) ? docIDInteger.intValue() : -1; int size = docAct.size(); for (int ind = 0; ind < size; ind++) { int id = docAct.get(ind); if (id == docID) { if (ind == 0) { // no change return false; } docAct.add(0, docAct.remove(ind)); return true; } } docAct.add(0, docIDInteger); return true; } private static Document getValidDoc(int ind, boolean forward) { consolidate(); int actSize = docAct.size(); while (ind >= 0 && ind < actSize) { int docID = docAct.get(ind); WeakReference wr = docRefs[docID]; Document doc = (wr != null) ? (Document)wr.get() : null; if (doc != null) { return doc; } ind += forward ? +1 : -1; } return null; } private static Document getNextActiveDoc(int docID, boolean forward) { consolidate(); int actSize = docAct.size(); int ind = forward ? 0 : (actSize - 1); while (ind >= 0 && ind < actSize) { if (docAct.get(ind) == docID) { ind += forward ? +1 : -1; // get next one return getValidDoc(ind, forward); } ind += forward ? +1 : -1; } return null; } private static JTextComponent getValidComp(int ind, boolean forward) { consolidate(); int actSize = compAct.size(); while (ind >= 0 && ind < actSize) { int compID = compAct.get(ind); WeakReference wr = compRefs[compID]; JTextComponent c = (wr != null) ? (JTextComponent)wr.get() : null; if (c != null) { return c; } ind += forward ? +1 : -1; } return null; } private static JTextComponent getNextActiveComp(int compID, boolean forward) { int actSize = compAct.size(); int ind = forward ? 0 : (actSize - 1); while (ind >= 0 && ind < actSize) { if (compAct.get(ind) == compID) { ind += forward ? +1 : -1; return getValidComp(ind, forward); } ind += forward ? +1 : -1; } return null; } private static void fireChange() { ChangeListener[] listeners = (ChangeListener[])listenerList.getListeners(ChangeListener.class); ChangeEvent evt = new ChangeEvent(DocumentsRegistry.class); for (int i = 0; i < listeners.length; i++) { listeners[i].stateChanged(evt); } } /** Debug the registry into string. */ public static synchronized String registryToString() { StringBuffer sb = new StringBuffer(); sb.append("Document References:\n"); // NOI18N for (int i = 0; i < docRefsCount; i++) { WeakReference wr = docRefs[i]; sb.append("docRefs[" + i + "]=" + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N } sb.append("Component References:\n"); // NOI18N for (int i = 0; i < compRefsCount; i++) { WeakReference wr = (WeakReference)compRefs[i]; sb.append("compRefs[" + i + "]=" + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N } sb.append("\nActive Document Indexes:\n"); // NOI18N for (int i = 0; i < docAct.size(); i++) { sb.append(docAct.get(i)); if (i != docAct.size() - 1) { sb.append(", "); // NOI18N } } sb.append("\nActive Component Indexes:\n"); // NOI18N for (int i = 0; i < compAct.size(); i++) { sb.append(compAct.get(i)); if (i != compAct.size() - 1) { sb.append(", "); // NOI18N } } return sb.toString(); } } File [added]: EditorImplementation.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/EditorImplementation.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 125 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.util.Collection; import java.util.ResourceBundle; import java.util.logging.Logger; import javax.swing.Action; import javax.swing.text.JTextComponent; import org.netbeans.spi.editor.EditorImplementationProvider; import org.openide.util.Lookup; import org.openide.util.NbBundle; /** This is provider of implementation. This package (org.netbeans.editor) * represent editor core which can be used independently on the rest of NetBeans. * However this core needs access to higher level functionality like access * to localized bundles, access to settings storage, etc. which can be implemented * differently by the applications which uses this editor core. For this purpose * was created this abstract class and it can be extended with any other methods which * are more and more often required by core editor. Example implementation * of this provider can be found in org.netbeans.modules.editor package * * @author David Konecny * @since 10/2001 */ public final class EditorImplementation { private static final Logger LOG = Logger.getLogger(EditorImplementation.class.getName()); private static final EditorImplementationProvider DEFAULT = new DefaultImplementationProvider(); private static EditorImplementation instance = null; private static EditorImplementationProvider externalProvider = null; private Lookup.Result result = null; /** Returns currently registered provider */ public static synchronized EditorImplementation getDefault() { if (instance == null) { instance = new EditorImplementation(); } return instance; } /** *

IMPORTANT: This method is here only for supporting the backwards * compatibility of the {@link org.netbeans.editor.DialogSupport} class. * */ public void setExternalProvider(EditorImplementationProvider provider) { this.externalProvider = provider; } /** Returns ResourceBundle for the given class.*/ public ResourceBundle getResourceBundle(String localizer) { return getProvider().getResourceBundle(localizer); } /** This is temporary method which allows core editor to access * glyph gutter action. These actions are then used when user clicks * on glyph gutter. In next version this should be removed and redesigned * as suggested in issue #16762 */ public Action[] getGlyphGutterActions(JTextComponent target) { return getProvider().getGlyphGutterActions(target); } /** Activates the given component or one of its ancestors. * @return whether the component or one of its ancestors was succesfuly activated * */ public boolean activateComponent(JTextComponent c) { return getProvider().activateComponent(c); } private EditorImplementation() { result = Lookup.getDefault().lookup( new Lookup.Template(EditorImplementationProvider.class)); } private EditorImplementationProvider getProvider() { if (externalProvider != null) { return externalProvider; } else { Collection providers = result.allInstances(); if (providers.isEmpty()) { LOG.warning("Can't find any EditorImplementationProvider; using default."); return DEFAULT; } else { return providers.iterator().next(); } } } private static final class DefaultImplementationProvider implements EditorImplementationProvider { private static final Action [] NOACTIONS = new Action[0]; public ResourceBundle getResourceBundle(String localizer) { return NbBundle.getBundle(localizer); } public Action[] getGlyphGutterActions(JTextComponent target) { return NOACTIONS; } public boolean activateComponent(JTextComponent c) { return false; } } // End of DefaultImplementationProvider class } File [added]: KeyEventBlocker.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/KeyEventBlocker.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 100 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2; import java.awt.Component; import java.awt.KeyboardFocusManager; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import java.util.LinkedList; import javax.swing.KeyStroke; import javax.swing.text.JTextComponent; /** * * @author Dusan Balek */ public class KeyEventBlocker implements KeyListener { private LinkedList blockedEvents = new LinkedList(); private JTextComponent component; private boolean discardKeyTyped = true; private static final boolean debugBlockEvent = Boolean.getBoolean("netbeans.debug.editor.blocker"); // NOI18N public KeyEventBlocker(JTextComponent component, boolean discardFirstKeyTypedEvent) { this.component = component; this.discardKeyTyped = discardFirstKeyTypedEvent; if (debugBlockEvent){ System.out.println(""); //NOI18N System.out.println("attaching listener"+this.component.getClass()+" - "+this.component.hashCode()); //NOI18N } this.component.addKeyListener(this); } /** Has to be called from AWT event thread to be properly synchronized */ public void stopBlocking(boolean dispatchBlockedEvents) { if (debugBlockEvent){ System.out.println("removing listener from "+this.component.getClass()+" - "+this.component.hashCode()); //NOI18N } this.component.removeKeyListener(this); if (dispatchBlockedEvents){ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); while(!blockedEvents.isEmpty()) { KeyEvent e = blockedEvents.removeFirst(); e = new KeyEvent((Component)e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), e.getKeyCode(), e.getKeyChar(), e.getKeyLocation()); kfm.dispatchEvent(e); } } } public void stopBlocking() { stopBlocking(true); } public void keyPressed(KeyEvent e) { if (debugBlockEvent){ System.out.println("consuming keyPressed event:"+KeyEvent.getKeyModifiersText(e.getModifiers())+" + "+KeyEvent.getKeyText(e.getKeyCode())); //NOI18N } e.consume(); blockedEvents.add(e); } public void keyReleased(KeyEvent e) { if (debugBlockEvent){ System.out.println("consuming keyReleased event:"+KeyEvent.getKeyModifiersText(e.getModifiers())+" + "+KeyEvent.getKeyText(e.getKeyCode())); //NOI18N } e.consume(); blockedEvents.add(e); } public void keyTyped(KeyEvent e) { if (debugBlockEvent){ System.out.println("consuming keyTyped event:"+KeyEvent.getKeyModifiersText(e.getModifiers())+" + "+KeyEvent.getKeyText(e.getKeyCode())); //NOI18N } e.consume(); if (discardKeyTyped) { discardKeyTyped = false; } else { blockedEvents.add(e); } } } Directory: /editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/ ========================================================================== File [added]: AbstractOffsetGapList.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/AbstractOffsetGapList.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 672 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.Collection; import org.netbeans.lib.editor.util.GapList; /** * The OffsetGapList is an extension of the gap list storing elements * containing an integer offset. * *

In order to perform well the implementation of this list stores its elements * in a sequential order sorted by their offset. This fact affects behavior of some * methods you might be familiar with from the List interface. * *

The list updates offsets of elements that it contains according to changes * notified by calling defaultInsertUpdate and defaultRemoveUpdate * methods. Usually this list is used for storing offsets in a java.swing.text.Document * and once an element with an offset is stored in the list the element's offset * is automatically updated when those two methods are called. It is the responsibility * of a user of this list to call the methods. * *

Updating offsets of many elements could be a time consuming task. In order * to minimize the overhead the list maintains so called offset gap. The * offset gap is a gap at the position of the last insert with the size of * Integer.MAX_VALUE / 2. When an element is added to the list its * offset gets adjusted and becomes so called raw offset. The relation * between offsets and raw offsets is following: * *

    *
  • offset < offsetGapStart : rawOffset = offset
  • *
  • offset > offsetGapStart : rawOffset = offset + offsetGapLength *
* * The equivalent formulas give a recipe for computing an offset from rawOffset: * *
    *
  • rawOffset < offsetGapStart : offset = rawOffset
  • *
  • rawOffset > offsetGapStart : offset = rawOffset - offsetGapLength *
* * When characteres are inserted at the position of the gap start * the gap simply gets shrinked and no offset have to be recomputed. Also, when * characters are removed just in front of the gap start the gap gets extended * and again no offset have to be recomputed. Offsets only have to be recomputed * when an insertion/removal happens not at the begginig of the gap. In that case * the gap has to be moved and offsets of any element, which is moved from 'behind' * the gap to a positoin in front of the gap, have to be recalculated. This algorithm * minimizes the number of recalculations needed for updating offsets when editing * a document under normal circumstances. * *

The maximum gap size is +1GB (Integer.MAX_VALUE / 2), which * should be enough for most of the cases. The gap size can't be bigger, because * raw offsets of elements behind the gap (i.e. with offset > offsetGapStart) * would exceed the maximum value of Integer and became negative * (i.e. less the zero). If that happend the offsets comparision would get broken. * *

This class also supports negative offsets up to a limit of -2GB. * * @author Miloslav Metelka * @version 1.00 */ public abstract class AbstractOffsetGapList extends GapList { private int offsetGapStart; // 28 bytes (24-super + 4) private int offsetGapLength = Integer.MAX_VALUE / 2; // 32 bytes public AbstractOffsetGapList() { } /** * Adds an element to this list. The element is * going to be added to at the position, which is appropriate for the * element's offset. * * @param element The element to add. * @return Always true. */ public final boolean add(E element) { int originalOffset = attachElement(element); setElementRawOffset(element, offset2raw(originalOffset)); int index = findOffsetIndex(originalOffset); super.add(index, element); return true; } /** * Adds an element to this list at the given position. If the * index is not an appropriate position for the element * to be added at this method will throw an exception. * * @param element The element to add. * @throws IndexOutOfBoundException If the index is not greater * or equal to zero and less or equal to the size of the list. * @throws IllegalStateException If adding the element at the * the index position would break sorting of the list. */ public final void add(int index, E element) { if (index < 0 || index > size()) { throw new IndexOutOfBoundsException("index = " + index + " size = " + size()); //NOI18N } int originalOffset = attachElement(element); setElementRawOffset(element, offset2raw(originalOffset)); boolean ok = true; // check offset of the element at (index - 1) if (index > 0 && elementOffset(index - 1) > originalOffset) { System.out.println("[" + (index - 1) + "] = " + elementOffset(index - 1) + " > {" + index + "} = "+ originalOffset); ok = false; } // check offset of the element at index if (index < size() && elementOffset(index) < originalOffset) { System.out.println("[" + index + "] = " + elementOffset(index) + " < {" + index + "} = "+ originalOffset); ok = false; } if (ok) { // the index is valid for the element super.add(index, element); } else { // can't insert the element at this index, the list must remain sorted detachElement(element); throw new IllegalStateException("Can't insert element at index: " + index); //NOI18N } } /** * Adds all elements from a collection. Calling this method is equivalent to * calling add(E) method for each element in the collection c. * * @return Always true. */ public final boolean addAll(Collection c) { for (E e : c) { add(e); } return true; } /** * This operation is not supported by this list. * * @throws UnsupportedOperationException */ public final boolean addAll(int index, Collection c) { throw new UnsupportedOperationException("This is an illegal operation on OffsetGapList."); //NOI18N } /** * This operation is not supported by this list. * * @throws UnsupportedOperationException */ public final boolean addArray(int index, Object[] elements) { throw new UnsupportedOperationException("This is an illegal operation on OffsetGapList."); //NOI18N } /** * This operation is not supported by this list. * * @throws UnsupportedOperationException */ public final boolean addArray(int index, Object[] elements, int off, int len) { throw new UnsupportedOperationException("This is an illegal operation on OffsetGapList."); //NOI18N } /** * Finds the position of the element in this list. * * @return The index of the element or -1 if the element can't * be find. */ public final int indexOf(Object o) { E element = getAttachedElement(o); if (element != null) { int offset = elementOffset(element); int idx = findElementIndex(offset); if (idx >= 0) { for (int i = idx; i < size() && elementOffset(i) == offset; i++) { if (element == get(i)) { return i; } } } } return -1; } /** * The same as the indexOf(E) method. The OffsetGapList * does not allow adding one element twice. * * @return The index of the element or -1 if the element can't * be find. */ public final int lastIndexOf(Object element) { return indexOf(element); } /** * Replaces an element of this list at the given position with a new element. * If the new element can't be placed at the index * position this method will throw an exception. * * @param index The position to add the element at. * @param element The element to add. * * @return The old elemet, which was originally stored at the index * position. */ public final E set(int index, E element) { if (index < 0 || index >= size()) { throw new IndexOutOfBoundsException("index = " + index + " size = " + size()); //NOI18N } int originalOffset = attachElement(element); setElementRawOffset(element, offset2raw(originalOffset)); boolean ok = true; // check offset of the element at (index - 1) if (index > 0 && elementOffset(index - 1) > originalOffset) { System.out.println("[" + (index - 1) + "] = " + elementOffset(index - 1) + " > {" + index + "} = "+ originalOffset); ok = false; } // check offset of the element at (index + 1) if (index + 1 < size() && elementOffset(index + 1) < originalOffset) { System.out.println("[" + (index + 1) + "] = " + elementOffset(index + 1) + " < {" + index + "} = "+ originalOffset); ok = false; } if (ok) { // the index is valid for the element E oldElement = super.set(index, element); detachElement(oldElement); return oldElement; } else { // can't insert the element at this index, the list must remain sorted detachElement(element); throw new IllegalStateException("Can't insert element at index: " + index); //NOI18N } } /** * This operation is not supported by this list. * * @throws UnsupportedOperationException */ public final void swap(int index1, int index2) { throw new UnsupportedOperationException("This is an illegal operation on OffsetGapList."); //NOI18N } /** * Gets the raw offset of the given element. * * @param elem The element to get the raw offset for. * @return The raw offset of the element. */ protected abstract int elementRawOffset(E elem); /** * Sets the raw offset of the given element currently stored in the list. * * @param elem element currently stored in the list. * @param rawOffset raw offset to be stored in the given element. */ protected abstract void setElementRawOffset(E elem, int rawOffset); /** * Gets called when an element is being added to the list. This is the first * method that will be called when an element is added to the list. The method * should return the original offset of the element. This offset will be * used for calculating element's raw offset and setting it by calling * the setElementRawOffset. Since that time the elemnt should * only keep its raw offset and use the raw2offset method for * translating it to the real offset again. * * @param elem The element being added to the list. * @return The original offset of the element (i.e. offset at the time when * the element is added to this list). */ protected abstract int attachElement(E elem); /** * Gets called when an element is removed from this list. After this method * is called the element should never try to use raw2offset to * get its real offset. The element is removed from the list and its raw offset * is no longer updated by the list and therefore can't be used for computing * the real offset. * * @param elem The element to detach from the list. */ protected abstract void detachElement(E elem); /** * Recognizes an object as an element of this list. If the object passed in * can be recognized as an element attached to this list the method should * return its type-casted version. If the object is not of a right type or * does not belong to this list the method should return null. * * @param o The object to recognize. * @return The type-casted version of the object (i.e. an element of this list) * or null if the element does not belong to the list. */ protected abstract E getAttachedElement(Object o); /** * Get the offset of the element stored in the list. *
* The raw offset stored in the element is preprocessed to become a real offset. * * @param elem element stored in the list. * @return offset of the element. */ protected int elementOffset(E elem) { return raw2Offset(elementRawOffset(elem)); } /** * Get the offset of the element stored in the list at the given index. *
* The raw offset stored in the element is preprocessed to become a real offset. * * @param index of the element in the list. * @return offset of the element. */ protected int elementOffset(int index) { return elementOffset(get(index)); } /** * Inform the list that there was an insert done into an underlying storage * (e.g. a swing document) which should move up offsets of the elements that have * their offset greater or equal to the insertion offset. * *

* Subclasses can build their own way of updating * and they are not required to use this method. * * @param offset offset at which the insertion occurred. * @param length length of the inserted area. */ public void defaultInsertUpdate(int offset, int length) { assert (length >= 0); if (offset != offsetGapStart()) { moveOffsetGap(offset, findOffsetIndex(offset)); } updateOffsetGapLength(-length); // Shrink the offset gap to simulate insertion // Optimize for subsequent insert by moving gap start to the end // of the just performed insertion. // This way the subsequent insert will not need to call moveOffsetGap() at all. // It's less optimal for insert-remove pairs (e.g. overwrite mode) // but they should be less frequent. updateOffsetGapStart(length); } /** * Inform the list that there was a removal done into an underlying storage * (e.g. a swing document) which should move down offsets of the elements * that have their offsets greater than the removal offset. *
* The offsets inside the removal area will be moved to its begining. *

* Subclasses can build their own way of updating * and they are not required to use this method. * * @param offset offset at which the removal occurred. * @param length length of the removed area. */ public void defaultRemoveUpdate(int offset, int length) { assert (length >= 0); int index = findOffsetIndex(offset); if (offset != offsetGapStart()) { moveOffsetGap(offset, index); } int size = size(); int removeAreaEndRawOffset = offset + offsetGapLength + length; // Move all elements inside the removed area to its end // so that after update of the offset gap length they appear // at the begining of the removal offset area while (index < size) { E elem = get(index++); if (elementRawOffset(elem) < removeAreaEndRawOffset) { setElementRawOffset(elem, removeAreaEndRawOffset); } else { // all subsequent offsets are higher break; } } updateOffsetGapLength(+length); } /** * Move the offset gap so that it's on the requested offset. *
* This method can be used when the index of the first element * at the given offset was precomputed already. * *

* Note: Improper use of this may logically damage * offsets of the elements contained in the list. * * @param offset offset to which the offsetGapStart * should be assigned. * @param index index of the first element in the list * that has an offset that is greater or equal that the given offset parameter. *
* It may be computed by {@link #findElementIndex(int)}. */ protected final void moveOffsetGap(int offset, int index) { if (offset < offsetGapStart) { // need to check items above index int bound = size(); for (int i = index; i < bound; i++) { E elem = get(i); int rawOffset = elementRawOffset(elem); if (rawOffset < offsetGapStart) { setElementRawOffset(elem, rawOffset + offsetGapLength); } else { break; } } } else { // check items below index for (int i = index - 1; i >= 0; i--) { E elem = get(i); int rawOffset = elementRawOffset(elem); if (rawOffset >= offsetGapStart) { setElementRawOffset(elem, rawOffset - offsetGapLength); } else { break; } } } offsetGapStart = offset; } /** * Obtain the start of the offset gap. * * @return start of the offset gap. */ protected final int offsetGapStart() { return offsetGapStart; } /** * Update the offset gap start by the given delta. *
* This may be needed e.g. after insertion/removal was done * in the document. * *

* Note: Improper use of this may logically damage * offsets of the elements contained in the list. */ protected final void updateOffsetGapStart(int offsetDelta) { offsetGapStart += offsetDelta; } /** * Obtain the length of the offset gap. * * @return length of the offset gap. */ protected final int offsetGapLength() { return offsetGapLength; } /** * Update the offset gap length by the given delta. *
* This may be needed e.g. after insertion/removal was done * in the document. * *

* Note: Improper use of this may logically damage * offsets of the elements contained in the list. */ protected final void updateOffsetGapLength(int offsetGapLengthDelta) { offsetGapLength += offsetGapLengthDelta; assert (offsetGapLength >= 0); // prevent overflow to negative numbers } /** * Find an index of the first element at the given offset in the list * by using binary search. * * @param offset offset of the element * @return index of the element. If there is no element with that * index then the index of the next element (with the greater offset) * (or size of the list) will be returned. *
* If there are multiple items with the same offset then the first one of them * will be returned. */ protected final int findOffsetIndex(int offset) { int index = findElementIndex(offset); return index < 0 ? -index - 1 : index; } /** * Finds an index of the first element at the given offset in the list * by using binary search. * * @param offset The offset of the element to find. * @param lowIdx The lowest index to look at. * @param highIdx The highest index to look at. * * @return index of the element with the given offset, * if it is contained in the list; * otherwise, (-(insertion point) - 1). The * insertion point is defined as the point at which an * element the offset would be inserted into the list: * the index of the first * element with its offset greater than the offset prameter, * or list.size(), if all * elements in the list have offsets less than the specified offset. * Note that this guarantees that the return value will be >= 0 if * and only if the element with the given offset is found. */ public final int findElementIndex(int offset, int lowIdx, int highIdx) { if (lowIdx < 0 || highIdx > size() - 1) { throw new IndexOutOfBoundsException("lowIdx = " + lowIdx + ", highIdx = " + highIdx + ", size = " + size()); } int low = lowIdx; int high = highIdx; while (low <= high) { int index = (low + high) >> 1; // mid in the binary search int elemOffset = elementOffset(index); if (elemOffset < offset) { low = index + 1; } else if (elemOffset > offset) { high = index - 1; } else { // exact offset found at index while (index > 0) { index--; if (elementOffset(index) < offset) { index++; break; } } return index; } } return -(low + 1); } public final int findElementIndex(int offset) { return findElementIndex(offset, 0, size() - 1); } /** * This method updates element's offset (shifts it above offset gap if necessary) * before adding the element to the list. * * This method should be called before (or after) the element is physically added * to the list. If the element is added below the offset gap * then calling of this method is not necessary. */ protected void updateElementOffsetAdd(E elem) { int rawOffset = elementRawOffset(elem); if (rawOffset >= offsetGapStart) { setElementRawOffset(elem, rawOffset + offsetGapLength); } } /** * This method updates element's offset (shifts it below offset gap if necessary) * before (or after) the element gets removed from the list. *
* This method should be called after the element is physically removed * from the list and it's desired that it retains its natural offset * (not possibly shifted by the offset gap length). *
* If the element was located below the offset gap prior removal * then calling of this method is not necessary. */ protected void updateElementOffsetRemove(E elem) { int rawOffset = elementRawOffset(elem); if (rawOffset >= offsetGapStart) { setElementRawOffset(elem, rawOffset - offsetGapLength); } } /** * Translate raw offset into real offset. * * @param rawOffset raw offset stored in an element. * @return real offset that the element is supposed to have. */ protected final int raw2Offset(int rawOffset) { return (rawOffset < offsetGapStart) ? rawOffset : rawOffset - offsetGapLength; } /** * Translate regular offset to raw offset. * * @param offset regular offset. * @return raw offset that can be used in elements. */ protected final int offset2raw(int offset) { return (offset < offsetGapStart) ? offset : offset + offsetGapLength; } protected void consistencyCheck() { super.consistencyCheck(); if (offsetGapLength < 0) { consistencyError("offsetGapLength < 0"); // NOI18N } int lastRawOffset = Integer.MIN_VALUE; int lastOffset = Integer.MIN_VALUE; int size = size(); for (int i = 0; i < size; i++) { E elem = get(i); int rawOffset = elementRawOffset(elem); int offset = raw2Offset(rawOffset); if (rawOffset < lastRawOffset) { consistencyError("Invalid rawOffset=" // NOI18N + rawOffset + " >= lastRawOffset=" + lastRawOffset // NOI18N + " at index=" + i // NOI18N ); } if (offset < lastOffset) { consistencyError("Invalid offset=" // NOI18N + offset + " >= lastOffset=" + lastOffset // NOI18N + " at index=" + i // NOI18N ); } lastRawOffset = rawOffset; lastOffset = offset; } } protected String dumpInternals() { return super.dumpInternals() + ", offGap(s=" + offsetGapStart + ", l=" + offsetGapLength + ")"; } } File [added]: BlockHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/BlockHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 125 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.modules.editor.lib2.search.EditorFindSupport; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.netbeans.spi.editor.highlighting.support.PositionsBag; import org.netbeans.spi.editor.highlighting.HighlightsSequence; /** * * @author vita */ public class BlockHighlighting extends AbstractHighlightsContainer implements HighlightsChangeListener { private static final Logger LOG = Logger.getLogger(BlockHighlighting.class.getName()); private String layerId; private JTextComponent component; private Document document; private PositionsBag bag; public BlockHighlighting(String layerId, JTextComponent component) { this.layerId = layerId; this.component = component; this.document = component.getDocument(); this.bag = new PositionsBag(document); this.bag.addHighlightsChangeListener(this); EditorFindSupport.getInstance().hookLayer(this, component); } public String getLayerTypeId() { return layerId; } public HighlightsSequence getHighlights(int startOffset, int endOffset) { return bag.getHighlights(startOffset, endOffset); } public void highlightChanged(HighlightsChangeEvent event) { fireHighlightsChange(event.getStartOffset(), event.getEndOffset()); } public void highlightBlock(final int startOffset, final int endOffset, final String coloringName) { document.render(new Runnable() { public void run() { if (startOffset < endOffset) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Highlighting block: [" + startOffset + ", " + endOffset + "]; " + getLayerTypeId()); } try { PositionsBag newBag = new PositionsBag(document); newBag.addHighlight( document.createPosition(startOffset), document.createPosition(endOffset), getAttribs(coloringName) ); bag.setHighlights(newBag); } catch (BadLocationException e) { LOG.log(Level.FINE, "Can't add highlight <" + startOffset + ", " + endOffset + ", " + coloringName + ">", e); } } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Reseting block highlighs; " + getLayerTypeId()); } bag.clear(); } } }); } public int [] gethighlightedBlock() { HighlightsSequence sequence = bag.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); if (sequence.moveNext()) { return new int [] { sequence.getStartOffset(), sequence.getEndOffset() }; } else { return null; } } private AttributeSet getAttribs(String coloringName) { FontColorSettings fcs = MimeLookup.getLookup( MimePath.parse(getMimeType())).lookup(FontColorSettings.class); AttributeSet attribs = fcs.getFontColors(coloringName); return attribs == null ? SimpleAttributeSet.EMPTY : attribs; } private String getMimeType() { return component.getUI().getEditorKit(component).getContentType(); } } File [added]: CaretRowHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/CaretRowHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 181 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.modules.editor.lib2.DocUtils; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; /** * The layer for highlighting a caret row. * * @author Vita Stejskal */ public class CaretRowHighlighting extends AbstractHighlightsContainer implements ChangeListener { private static final Logger LOG = Logger.getLogger(CaretRowHighlighting.class.getName()); public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.CaretRowHighlighting"; //NOI18N private JTextComponent component; private Position currentLineStart; private Position currentLineEnd; /** Creates a new instance of CaretSelectionLayer */ public CaretRowHighlighting(JTextComponent component) { this.component = component; Position [] currentLine = getCurrentLine(component.getCaret().getDot()); currentLineStart = currentLine[0]; currentLineEnd = currentLine[1]; this.component.getCaret().addChangeListener(this); } public HighlightsSequence getHighlights(int startOffset, int endOffset) { if ((startOffset <= currentLineStart.getOffset() && endOffset > currentLineStart.getOffset()) || (endOffset >= currentLineEnd.getOffset() && startOffset < currentLineEnd.getOffset())) { return new SimpleHighlightsSequence(currentLineStart, currentLineEnd, getAttribs()); } else { return new SimpleHighlightsSequence(); } } public void stateChanged(ChangeEvent e) { Position [] currentLine = getCurrentLine(((Caret) e.getSource()).getDot()); if (currentLine[0].getOffset() != currentLineStart.getOffset() || currentLine[1].getOffset() != currentLineEnd.getOffset()) { Position changeStart = currentLine[0].getOffset() < currentLineStart.getOffset() ? currentLine[0] : currentLineStart; Position changeEnd = currentLine[1].getOffset() > currentLineEnd.getOffset() ? currentLine[1] : currentLineEnd; if (LOG.isLoggable(Level.FINE)) { LOG.fine("Current row changed from [" + currentLineStart.getOffset() + ", " + currentLineEnd.getOffset()+ "] to [" + currentLine[0].getOffset() + ", " + currentLine[1].getOffset() + "]"); } currentLineStart = currentLine[0]; currentLineEnd = currentLine[1]; fireHighlightsChange(changeStart.getOffset(), changeEnd.getOffset()); } } private Position[] getCurrentLine(int caretOffset) { Document document = component.getDocument(); try { int startOffset = DocUtils.getRowStart(document, caretOffset, 0); int endOffset = DocUtils.getRowEnd(document, caretOffset); if (endOffset < document.getLength()) { endOffset++; // include the new-line character } return new Position [] { document.createPosition(startOffset), document.createPosition(endOffset), }; } catch (BadLocationException e) { LOG.log(Level.WARNING, e.getMessage(), e); return new Position [] { document.getStartPosition(), document.getEndPosition(), }; } } private AttributeSet getAttribs() { FontColorSettings fcs = MimeLookup.getLookup( MimePath.parse(getMimeType())).lookup(FontColorSettings.class); AttributeSet attribs = fcs.getFontColors(FontColorNames.CARET_ROW_COLORING); if (attribs == null) { attribs = SimpleAttributeSet.EMPTY; } else { attribs = AttributesUtilities.createImmutable( attribs, AttributesUtilities.createImmutable(ATTR_EXTENDS_EOL, Boolean.TRUE) ); } return attribs; } private String getMimeType() { return component.getUI().getEditorKit(component).getContentType(); } private static final class SimpleHighlightsSequence implements HighlightsSequence { private Position startPosition; private Position endPosition; private AttributeSet attribs; private boolean end = false; public SimpleHighlightsSequence() { end = true; } public SimpleHighlightsSequence(Position startPosition, Position endPosition, AttributeSet attribs) { this.startPosition = startPosition; this.endPosition = endPosition; this.attribs = attribs; } public boolean moveNext() { if (!end) { end = true; return true; } else { return false; } } public int getStartOffset() { return startPosition.getOffset(); } public int getEndOffset() { return endPosition.getOffset(); } public AttributeSet getAttributes() { return attribs; } } // End of SimpleHighlightsSequence } File [added]: CompoundHighlightsContainer.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/CompoundHighlightsContainer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 353 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.lang.ref.WeakReference; import java.util.ConcurrentModificationException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Position; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; /** * * @author Vita Stejskal, Miloslav Metelka */ public final class CompoundHighlightsContainer extends AbstractHighlightsContainer { private static final Logger LOG = Logger.getLogger(CompoundHighlightsContainer.class.getName()); private static final Position MAX_POSITION = new Position() { public int getOffset() { return Integer.MAX_VALUE; } }; private static final int MIN_CACHE_SIZE = 128; private Document doc; private HighlightsContainer[] layers; private long version = 0; private final String LOCK = new String("CompoundHighlightsContainer.LOCK"); //NOI18N private final LayerListener listener = new LayerListener(this); private OffsetsBag cache; private Position cacheLowestPos; private Position cacheHighestPos; public CompoundHighlightsContainer() { this(null, null); } public CompoundHighlightsContainer(Document doc, HighlightsContainer[] layers) { setLayers(doc, layers); } /** * Gets the list of Highlights from this layer in the specified * area. The highlights are obtained as a merge of the highlights from all the * delegate layers. The following rules must hold true for the parameters * passed in: * *

    *
  • 0 <= startOffset <= endOffset
  • *
  • 0 <= endOffset <= document.getLength() - 1
  • *
  • Optionally, endOffset can be equal to Integer.MAX_VALUE * in which case all available highlights will be returned.
  • *
* * @param startOffset The beginning of the area. * @param endOffset The end of the area. * * @return The Highlights in the area between startOffset * and endOffset. */ public HighlightsSequence getHighlights(int startOffset, int endOffset) { assert 0 <= startOffset : "offsets must be greater than or equal to zero"; //NOI18N assert startOffset <= endOffset : "startOffset must be less than or equal to endOffset; " + //NOI18N "startOffset = " + startOffset + " endOffset = " + endOffset; //NOI18N synchronized (LOCK) { if (doc == null || layers == null || layers.length == 0 || startOffset == endOffset) { return HighlightsSequence.EMPTY; } int [] update = null; int lowest = cacheLowestPos == null ? -1 : cacheLowestPos.getOffset(); int highest = cacheHighestPos == null ? -1 : cacheHighestPos.getOffset(); if (lowest == -1 || highest == -1) { // not sure what is cached -> reset the cache cache = null; } else { int maxDistance = Math.max(MIN_CACHE_SIZE, highest - lowest); if (endOffset > lowest - maxDistance && endOffset <= highest && startOffset < lowest) { // below the cached area, but close enough update = new int [] { startOffset, lowest }; } else if (startOffset < highest + maxDistance && startOffset >= lowest && endOffset > highest) { // above the cached area, but close enough update = new int [] { highest, endOffset }; } else if (startOffset < lowest && endOffset > highest) { // extends the cached area on both sides update = new int [] { startOffset, lowest, highest, endOffset }; } else if (startOffset >= lowest && endOffset <= highest) { // inside the cached area } else { // completely off the area and too far cache = null; } } if (cache == null) { cache = new OffsetsBag(doc, true); lowest = highest = -1; update = new int [] { startOffset, endOffset }; } if (update != null) { for (int i = 0; i < update.length / 2; i++) { if (update[2 * i + 1] - update[2 * i] < MIN_CACHE_SIZE) { update[2 * i + 1] = update[2 * i] + MIN_CACHE_SIZE; if (update[2 * i + 1] >= doc.getLength()) { update[2 * i + 1] = Integer.MAX_VALUE; } } updateCache(update[2 * i], update[2 * i + 1]); if (update[2 * i + 1] == Integer.MAX_VALUE) { break; } } if (lowest == -1 || highest == -1) { cacheLowestPos = createPosition(update[0]); cacheHighestPos = createPosition(update[update.length - 1]); } else { cacheLowestPos = createPosition(Math.min(lowest, update[0])); cacheHighestPos = createPosition(Math.max(highest, update[update.length - 1])); } if (LOG.isLoggable(Level.FINE)) { LOG.fine("Cache boundaries: " + //NOI18N "<" + (cacheLowestPos == null ? "-" : cacheLowestPos.getOffset()) + //NOI18N ", " + (cacheHighestPos == null ? "-" : cacheHighestPos.getOffset()) + "> " + //NOI18N "when asked for <" + startOffset + ", " + endOffset + ">"); //NOI18N } } return new Seq(version, cache.getHighlights(startOffset, endOffset)); } } /** * Gets the delegate layers. * * @return The layers, which this proxy layer delegates to. */ public HighlightsContainer[] getLayers() { synchronized (LOCK) { return layers; } } /** * Sets the delegate layers. The layers are merged in the same order in which * they appear in the array passed into this method. That means that the first * layer in the array is the less important (i.e. the bottom of the z-order) and * the last layer in the array is the most visible one (i.e. the top of the z-order). * *

If you want the layers to be merged according to their real z-order sort * the array first by using ZOrder.sort(). * * @param layers The new delegate layers. Can be null. * @see org.netbeans.api.editor.view.ZOrder#sort(HighlightLayer []) */ public void setLayers(Document doc, HighlightsContainer[] layers) { Document docForEvents = null; synchronized (LOCK) { if (doc == null) { assert layers == null : "If doc is null the layers must be null too."; //NOI18N } docForEvents = doc != null ? doc : this.doc; // Remove the listener from the current layers if (this.layers != null) { for (int i = 0; i < this.layers.length; i++) { this.layers[i].removeHighlightsChangeListener(listener); } } this.doc = doc; this.layers = layers; cache = null; version++; // Add the listener to the new layers if (this.layers != null) { for (int i = 0; i < this.layers.length; i++) { this.layers[i].addHighlightsChangeListener(listener); } } } if (docForEvents != null) { docForEvents.render(new Runnable() { public void run() { fireHighlightsChange(0, Integer.MAX_VALUE); } }); } } public void resetCache() { layerChanged(null, 0, Integer.MAX_VALUE); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private void layerChanged(HighlightsContainer layer, final int changeStartOffset, final int changeEndOffset) { Document docForEvents = null; synchronized (LOCK) { // XXX: Perhaps we could do something more efficient. cache = null; version++; docForEvents = doc; } // Fire an event if (docForEvents != null) { docForEvents.render(new Runnable() { public void run() { fireHighlightsChange(changeStartOffset, changeEndOffset); } }); } } private void updateCache(int startOffset, int endOffset) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Updating cache: <" + startOffset + ", " + endOffset + ">"); //NOI18N } for (HighlightsContainer layer : layers) { HighlightsSequence seq = layer.getHighlights(startOffset, endOffset); cache.addAllHighlights(seq); } } private Position createPosition(int offset) { try { if (offset == Integer.MAX_VALUE) { return MAX_POSITION; } else { return doc.createPosition(offset); } } catch (BadLocationException e) { LOG.log(Level.WARNING, "Can't create document position: offset = " + offset + //NOI18N ", document.lenght = " + doc.getLength(), e); //NOI18N return null; } } private static final class LayerListener implements HighlightsChangeListener { private WeakReference ref; public LayerListener(CompoundHighlightsContainer container) { ref = new WeakReference(container); } public void highlightChanged(HighlightsChangeEvent event) { CompoundHighlightsContainer container = ref.get(); if (container != null) { container.layerChanged( (HighlightsContainer)event.getSource(), event.getStartOffset(), event.getEndOffset()); } } } // End of Listener class private final class Seq implements HighlightsSequence { private HighlightsSequence seq; private long version; public Seq(long version, HighlightsSequence seq) { this.version = version; this.seq = seq; } public boolean moveNext() { synchronized (CompoundHighlightsContainer.this.LOCK) { checkVersion(); return seq.moveNext(); } } public int getStartOffset() { synchronized (CompoundHighlightsContainer.this.LOCK) { checkVersion(); return seq.getStartOffset(); } } public int getEndOffset() { synchronized (CompoundHighlightsContainer.this.LOCK) { checkVersion(); return seq.getEndOffset(); } } public AttributeSet getAttributes() { synchronized (CompoundHighlightsContainer.this.LOCK) { checkVersion(); return seq.getAttributes(); } } private void checkVersion() { if (this.version != CompoundHighlightsContainer.this.version) { throw new ConcurrentModificationException(); } } } // End of Seq class } File [added]: Factory.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/Factory.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 96 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.ArrayList; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.ZOrder; /** * The factory for editor default highlighting layers. * * @author Vita Stejskal */ public class Factory implements HighlightsLayerFactory { /** A unique identifier of the block search layer type. */ public static final String BLOCK_SEARCH_LAYER = "org.netbeans.modules.editor.lib2.highlighting.BlockHighlighting/BLOCK_SEARCH"; //NOI18N /** A unique identifier of the incremental search layer type. */ public static final String INC_SEARCH_LAYER = "org.netbeans.modules.editor.lib2.highlighting.BlockHighlighting/INC_SEARCH"; //NOI18N /** Creates a new instance of Factory */ public Factory() { } public HighlightsLayer[] createLayers(HighlightsLayerFactory.Context context) { ArrayList layers = new ArrayList(); layers.add(HighlightsLayer.create( CaretRowHighlighting.LAYER_TYPE_ID, ZOrder.CARET_RACK, true, new CaretRowHighlighting(context.getComponent())) ); layers.add(HighlightsLayer.create( TextSelectionHighlighting.LAYER_TYPE_ID, ZOrder.SHOW_OFF_RACK.aboveLayers(CaretRowHighlighting.LAYER_TYPE_ID), true, new TextSelectionHighlighting(context.getComponent())) ); layers.add(HighlightsLayer.create( BLOCK_SEARCH_LAYER, ZOrder.SHOW_OFF_RACK.aboveLayers(CaretRowHighlighting.LAYER_TYPE_ID), true, new BlockHighlighting(BLOCK_SEARCH_LAYER, context.getComponent())) ); layers.add(HighlightsLayer.create( TextSearchHighlighting.LAYER_TYPE_ID, ZOrder.SHOW_OFF_RACK.aboveLayers(BLOCK_SEARCH_LAYER), true, new TextSearchHighlighting(context.getComponent())) ); layers.add(HighlightsLayer.create( INC_SEARCH_LAYER, ZOrder.SHOW_OFF_RACK.aboveLayers(TextSearchHighlighting.LAYER_TYPE_ID).belowLayers(TextSelectionHighlighting.LAYER_TYPE_ID), true, new BlockHighlighting(INC_SEARCH_LAYER, context.getComponent())) ); // If there is a lexer for the document create lexer-based syntax highlighting if (TokenHierarchy.get(context.getDocument()) != null) { layers.add(HighlightsLayer.create( SyntaxHighlighting.LAYER_TYPE_ID, ZOrder.SYNTAX_RACK, true, new SyntaxHighlighting(context.getDocument())) ); } return layers.toArray(new HighlightsLayer [layers.size()]); } } File [added]: HighlightingManager.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingManager.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 427 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.openide.ErrorManager; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.TopologicalSortException; import org.openide.util.Utilities; import org.openide.util.WeakListeners; import org.openide.util.lookup.ProxyLookup; /** * * @author Vita Stejskal */ public final class HighlightingManager { private static final Logger LOG = Logger.getLogger(HighlightingManager.class.getName()); public static synchronized HighlightingManager getInstance() { if (instance == null) { instance = new HighlightingManager(); } return instance; } public HighlightsContainer getHighlights(JTextComponent pane, HighlightsLayerFilter filter) { return getHighlighting(pane).getContainer(filter); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private static HighlightingManager instance; /* package */ final WeakHashMap> CACHE = new WeakHashMap>(); /** Creates a new instance of HighlightingManager */ private HighlightingManager() { } private Highlighting getHighlighting(JTextComponent pane) { synchronized (CACHE) { WeakReference ref = CACHE.get(pane); Highlighting h = ref == null ? null : ref.get(); if (h == null) { h = new Highlighting(pane); CACHE.put(pane, new WeakReference(h)); } return h; } } private static final class Highlighting implements PropertyChangeListener { private static final String PROP_MIME_TYPE = "mimeType"; //NOI18N private static final String PROP_DOCUMENT = "document"; //NOI18N private static final String PROP_HL_INCLUDES = "HighlightsLayerIncludes"; //NOI18N private static final String PROP_HL_EXCLUDES = "HighlightsLayerExcludes"; //NOI18N // The factories changes tracking private Lookup.Result factories = null; private LookupListener factoriesTracker = new LookupListener() { public void resultChanged(LookupEvent ev) { rebuildAllContainers(); } }; // The FontColorSettings changes tracking private Lookup.Result settings = null; private LookupListener settingsTracker = new LookupListener() { public void resultChanged(LookupEvent ev) { // System.out.println("Settings tracker for '" + (lastKnownMimePaths == null ? "null" : lastKnownMimePaths[0].getPath()) + "'"); resetAllContainers(); } }; private final JTextComponent pane; private HighlightsLayerFilter paneFilter; private Document lastKnownDocument = null; private MimePath [] lastKnownMimePaths = null; private final WeakHashMap> containers = new WeakHashMap>(); public Highlighting(JTextComponent pane) { this.pane = pane; this.paneFilter = new RegExpFilter(pane.getClientProperty(PROP_HL_INCLUDES), pane.getClientProperty(PROP_HL_EXCLUDES)); this.pane.addPropertyChangeListener(WeakListeners.propertyChange(this, pane)); rebuildAll(); } public synchronized HighlightsContainer getContainer(HighlightsLayerFilter filter) { WeakReference ref = containers.get(filter); CompoundHighlightsContainer container = ref == null ? null : ref.get(); if (container == null) { container = new CompoundHighlightsContainer(); rebuildContainer(filter, container); containers.put(filter, new WeakReference(container)); } return container; } // ---------------------------------------------------------------------- // PropertyChangeListener implementation // ---------------------------------------------------------------------- public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == null || PROP_DOCUMENT.equals(evt.getPropertyName())) { rebuildAll(); } if (PROP_HL_INCLUDES.equals(evt.getPropertyName()) || PROP_HL_EXCLUDES.equals(evt.getPropertyName())) { synchronized (this) { paneFilter = new RegExpFilter(pane.getClientProperty(PROP_HL_INCLUDES), pane.getClientProperty(PROP_HL_EXCLUDES)); rebuildAllContainers(); } } } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private MimePath [] getAllDocumentMimePath() { Document doc = pane.getDocument(); String mainMimeType; Object propMimeType = doc.getProperty(PROP_MIME_TYPE); if (propMimeType != null) { mainMimeType = propMimeType.toString(); } else { mainMimeType = pane.getUI().getEditorKit(pane).getContentType(); } return new MimePath [] { MimePath.parse(mainMimeType) }; } private synchronized void rebuildAll() { // Get the new set of mime path MimePath [] mimePaths = getAllDocumentMimePath(); // Recalculate factories and all containers if needed if (!Utilities.compareObjects(lastKnownDocument, pane.getDocument()) || !Arrays.equals(lastKnownMimePaths, mimePaths) ) { // Unregister listeners if (factories != null) { factories.removeLookupListener(factoriesTracker); } if (settings != null) { settings.removeLookupListener(settingsTracker); } if (mimePaths != null) { ArrayList lookups = new ArrayList(); for(MimePath mimePath : mimePaths) { lookups.add(MimeLookup.getLookup(mimePath)); } ProxyLookup lookup = new ProxyLookup(lookups.toArray(new Lookup[lookups.size()])); factories = lookup.lookup(new Lookup.Template(HighlightsLayerFactory.class)); settings = lookup.lookup(new Lookup.Template(FontColorSettings.class)); } else { factories = null; settings = null; } // Start listening again if (factories != null) { factories.addLookupListener(factoriesTracker); factories.allItems(); // otherwise we won't get any events at all } if (settings != null) { settings.addLookupListener(settingsTracker); settings.allItems(); // otherwise we won't get any events at all } lastKnownDocument = pane.getDocument(); lastKnownMimePaths = mimePaths; rebuildAllContainers(); } } private synchronized void resetAllContainers() { for(HighlightsLayerFilter filter : containers.keySet()) { WeakReference ref = containers.get(filter); CompoundHighlightsContainer container = ref == null ? null : ref.get(); if (container != null) { container.resetCache(); } } } private synchronized void rebuildAllContainers() { for(HighlightsLayerFilter filter : containers.keySet()) { WeakReference ref = containers.get(filter); CompoundHighlightsContainer container = ref == null ? null : ref.get(); if (container != null) { rebuildContainer(filter, container); } } } private synchronized void rebuildContainer(HighlightsLayerFilter filter, CompoundHighlightsContainer container) { if (factories != null) { Document doc = pane.getDocument(); Collection all = factories.allInstances(); HashMap layers = new HashMap(); HighlightsLayerFactory.Context context = HighlightingSpiPackageAccessor.get().createFactoryContext(doc, pane); for(HighlightsLayerFactory factory : all) { HighlightsLayer [] factoryLayers = factory.createLayers(context); if (factoryLayers == null) { continue; } for(HighlightsLayer layer : factoryLayers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); String layerTypeId = layerAccessor.getLayerTypeId(); if (!layers.containsKey(layerTypeId)) { layers.put(layerTypeId, layer); } } } // Sort the layers by their z-order List sortedLayers; try { sortedLayers = HighlightingSpiPackageAccessor.get().sort(layers.values()); } catch (TopologicalSortException tse) { ErrorManager.getDefault().notify(tse); @SuppressWarnings("unchecked") //NOI18N List sl = (List)tse.partialSort(); sortedLayers = sl; } // filter the layers sortedLayers = paneFilter.filterLayers(Collections.unmodifiableList(sortedLayers)); sortedLayers = filter.filterLayers(Collections.unmodifiableList(sortedLayers)); // Get the containers ArrayList hcs = new ArrayList(); for(HighlightsLayer layer : sortedLayers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); hcs.add(layerAccessor.getContainer()); } if (LOG.isLoggable(Level.FINE)) { logLayers(doc, lastKnownMimePaths, sortedLayers); } container.setLayers(doc, hcs.toArray(new HighlightsContainer[hcs.size()])); } else { container.setLayers(null, null); } } private static void logLayers(Document doc, MimePath [] mimePaths, List layers) { StringBuilder sb = new StringBuilder(); sb.append("HighlighsLayers {\n"); //NOI18N sb.append(" * document : "); //NOI18N sb.append(doc.toString()); sb.append("\n"); //NOI18N sb.append(" * mime paths : \n"); //NOI18N for(MimePath mimePath : mimePaths) { sb.append(" "); //NOI18N sb.append(mimePath.getPath()); sb.append("\n"); //NOI18N } sb.append(" * layers : \n"); //NOI18N for(HighlightsLayer layer : layers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); sb.append(" "); //NOI18N sb.append(layerAccessor.getLayerTypeId()); sb.append("\n"); //NOI18N } sb.append("}\n"); //NOI18N LOG.fine(sb.toString()); } } // End of Highlighting class private static final class RegExpFilter implements HighlightsLayerFilter { private final List includes; private final List excludes; public RegExpFilter(Object includes, Object excludes) { this.includes = buildPatterns(includes); this.excludes = buildPatterns(excludes); } public List filterLayers(List layers) { List includedLayers; if (includes.isEmpty()) { includedLayers = layers; } else { includedLayers = filter(layers, includes, true); } List filteredLayers; if (excludes.isEmpty()) { filteredLayers = includedLayers; } else { filteredLayers = filter(includedLayers, excludes, false); } return filteredLayers; } private static List filter( List layers, List patterns, boolean includeMatches // true means include matching layers, false means include non-matching layers ) { List filtered = new ArrayList(); for(HighlightsLayer layer : layers) { HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); for(Pattern pattern : patterns) { boolean matches = pattern.matcher(layerAccessor.getLayerTypeId()).matches(); if (matches && includeMatches) { filtered.add(layer); } if (!matches && !includeMatches) { filtered.add(layer); } } } return filtered; } private static List buildPatterns(Object expressions) { List patterns = new ArrayList(); if (expressions instanceof String) { try { patterns.add(Pattern.compile((String) expressions)); } catch (PatternSyntaxException e) { LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); //NOI18N } } else if (expressions instanceof String[]) { for(String expression : (String []) expressions) { try { patterns.add(Pattern.compile(expression)); } catch (PatternSyntaxException e) { LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); //NOI18N } } } return patterns; } } // End of RegExpFilter class } File [added]: HighlightingSpiPackageAccessor.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingSpiPackageAccessor.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 67 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.Collection; import java.util.List; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.openide.util.TopologicalSortException; /** * * @author vita */ public abstract class HighlightingSpiPackageAccessor { private static HighlightingSpiPackageAccessor ACCESSOR = null; public static synchronized void register(HighlightingSpiPackageAccessor accessor) { assert ACCESSOR == null : "Can't register two package accessors!"; ACCESSOR = accessor; } public static synchronized HighlightingSpiPackageAccessor get() { // Trying to wake up HighlightsLayer ... try { Class clazz = Class.forName(HighlightsLayer.class.getName()); } catch (ClassNotFoundException e) { // ignore } assert ACCESSOR != null : "There is no package accessor available!"; return ACCESSOR; } /** Creates a new instance of HighlightingSpiPackageAccessor */ protected HighlightingSpiPackageAccessor() { } public abstract HighlightsLayerFactory.Context createFactoryContext(Document document, JTextComponent component); public abstract List sort(Collection layers) throws TopologicalSortException; public abstract HighlightsLayerAccessor getHighlightsLayerAccessor(HighlightsLayer layer); public abstract List dismantle(AttributeSet set); } File [added]: HighlightsLayerAccessor.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsLayerAccessor.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 14 --------------- package org.netbeans.modules.editor.lib2.highlighting; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.ZOrder; public interface HighlightsLayerAccessor { public String getLayerTypeId(); public boolean isFixedSize(); public ZOrder getZOrder(); public HighlightsContainer getContainer(); } // End of HighlightsLayerAccessor File [added]: HighlightsLayerFilter.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsLayerFilter.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 39 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.List; import org.netbeans.spi.editor.highlighting.HighlightsLayer; /** * * @author vita */ public interface HighlightsLayerFilter { public static final HighlightsLayerFilter IDENTITY = new HighlightsLayerFilter() { public List filterLayers(List layers) { return layers; } }; public List filterLayers(List layers); } File [added]: OffsetGapList.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/OffsetGapList.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 120 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; /** * The implementation of AbstractOffsetGapList with * Offset elements. * * @author Vita Stejskal */ public final class OffsetGapList extends AbstractOffsetGapList { /** Creates a new instance of SimpleOffsetGapList */ public OffsetGapList() { } protected int elementRawOffset(E elem) { return elem.getRawOffset(); } protected void setElementRawOffset(E elem, int rawOffset) { elem.setRawOffset(rawOffset); } protected int attachElement(E elem) { return elem.attach(this); } protected void detachElement(E elem) { elem.detach(this); } protected E getAttachedElement(Object o) { if ((o instanceof Offset) && ((Offset) o).checkOwner(this)) { @SuppressWarnings("unchecked") //NOI18N E element = (E) o; return element; } else { return null; } } /** * An offset gap list element. The OffsetGapList can accomodate * either instances of this class or any of its subclass. */ public static class Offset { private int originalOrRawOffset; private OffsetGapList list; /** * Creates a new Offset object and sets its original offset * to the value passed in. * * @param offset The original offset of this Offset object. */ public Offset(int offset) { this.originalOrRawOffset = offset; } /** * Gets the offset of this Offset object. The offset is * either the original offset passed to the constructor if this Offset * instance has not been attached to a list yet or it is the real * offset of this instance, which reflects all offset updates in the list * (i.e. it gets updated when {@link AbstractOffsetGapList#defaultInsertUpdate} or * {@link AbstractOffsetGapList#defaultRemoveUpdate} is called). * * @return The offset of this Offset instance. */ public final int getOffset() { if (list == null) { return originalOrRawOffset; } else { return list.raw2Offset(getRawOffset()); } } private int attach(OffsetGapList list) { assert this.list == null : "Offset instances can only be added to one OffsetGapList."; //NOI18N this.list = list; return originalOrRawOffset; } private void detach(OffsetGapList list) { assert this.list == list : "Can't detach from a foreign list."; //NOI18N this.list = null; } private boolean checkOwner(OffsetGapList list) { return this.list == list; } private int getRawOffset() { return originalOrRawOffset; } private void setRawOffset(int rawOffset) { this.originalOrRawOffset = rawOffset; } } // End of Offset class } File [added]: ProxyHighlightsContainer.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/ProxyHighlightsContainer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 388 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; /** * * @author Vita Stejskal, Miloslav Metelka */ public final class ProxyHighlightsContainer extends AbstractHighlightsContainer { private static final Logger LOG = Logger.getLogger(ProxyHighlightsContainer.class.getName()); private HighlightsContainer[] layers; private long version = 0; private final String LOCK = new String("ProxyHighlightsContainer.LOCK"); //NOI18N private final LayerListener listener = new LayerListener(this); public ProxyHighlightsContainer() { this(null); } public ProxyHighlightsContainer(HighlightsContainer[] layers) { setLayers(layers); } /** * Gets the list of Highlights from this layer in the specified * area. The highlights are obtained as a merge of the highlights from all the * delegate layers. The following rules must hold true for the parameters * passed in: * *

    *
  • 0 <= startOffset <= endOffset
  • *
  • 0 <= endOffset <= document.getLength() - 1
  • *
  • Optionally, endOffset can be equal to Integer.MAX_VALUE * in which case all available highlights will be returned.
  • *
* * @param startOffset The beginning of the area. * @param endOffset The end of the area. * * @return The Highlights in the area between startOffset * and endOffset. */ public HighlightsSequence getHighlights(int startOffset, int endOffset) { assert 0 <= startOffset : "offsets must be greater than or equal to zero"; //NOI18N assert startOffset <= endOffset : "startOffset must be less than or equal to endOffset; " + //NOI18N "startOffset = " + startOffset + " endOffset = " + endOffset; //NOI18N synchronized (LOCK) { if (layers == null || layers.length == 0 || startOffset == endOffset) { return HighlightsSequence.EMPTY; } HighlightsSequence seq [] = new HighlightsSequence[layers.length]; for(int i = 0; i < layers.length; i++) { seq[i] = layers[layers.length - i - 1].getHighlights(startOffset, endOffset); } return new ProxySeq(version, seq, startOffset, endOffset); } } /** * Gets the delegate layers. * * @return The layers, which this proxy layer delegates to. */ public HighlightsContainer[] getLayers() { synchronized (LOCK) { return layers; } } /** * Sets the delegate layers. The layers are merged in the same order in which * they appear in the array passed into this method. That means that the first * layer in the array is the less important (i.e. the bottom of the z-order) and * the last layer in the array is the most visible one (i.e. the top of the z-order). * *

If you want the layers to be merged according to their real z-order sort * the array first by using ZOrder.sort(). * * @param layers The new delegate layers. Can be null. * @see org.netbeans.api.editor.view.ZOrder#sort(HighlightLayer []) */ public void setLayers(HighlightsContainer[] layers) { synchronized (LOCK) { // Remove the listener from the current layers if (this.layers != null) { for (int i = 0; i < this.layers.length; i++) { this.layers[i].removeHighlightsChangeListener(listener); } } this.layers = layers; this.version++; // Add the listener to the new layers if (this.layers != null) { for (int i = 0; i < this.layers.length; i++) { this.layers[i].addHighlightsChangeListener(listener); } } } fireHighlightsChange(0, Integer.MAX_VALUE); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private void layerChanged(HighlightsContainer layer, int changeStartOffset, int changeEndOffset) { synchronized (LOCK) { version++; } // Fire an event fireHighlightsChange(changeStartOffset, changeEndOffset); } private static final class LayerListener implements HighlightsChangeListener { private WeakReference ref; public LayerListener(ProxyHighlightsContainer container) { ref = new WeakReference(container); } public void highlightChanged(HighlightsChangeEvent event) { ProxyHighlightsContainer container = ref.get(); if (container != null) { container.layerChanged( (HighlightsContainer)event.getSource(), event.getStartOffset(), event.getEndOffset()); } } } // End of Listener class private final class ProxySeq implements HighlightsSequence { private Sequence2Marks [] marks; private int index1 = -1; private int index2 = -1; private AttributeSet compositeAttributes = null; private long version; public ProxySeq(long version, HighlightsSequence [] seq, int startOffset, int endOffset) { this.version = version; // Initialize marks marks = new Sequence2Marks [seq.length]; for (int i = 0; i < seq.length; i++) { marks[i] = new Sequence2Marks(seq[i], startOffset, endOffset); marks[i].moveNext(); } this.index2 = findLowest(); } public boolean moveNext() { synchronized (ProxyHighlightsContainer.this.LOCK) { checkVersion(); do { // Move to the next mark index1 = index2; if (index2 != -1) { marks[index2].moveNext(); index2 = findLowest(); } if (index1 == -1 || index2 == -1) { break; } compositeAttributes = findAttributes(); } while (compositeAttributes == null); return index1 != -1 && index2 != -1; } } public int getStartOffset() { synchronized (ProxyHighlightsContainer.this.LOCK) { checkVersion(); if (index1 == -1 || index2 == -1) { throw new NoSuchElementException(); } return marks[index1].getPreviousMarkOffset(); } } public int getEndOffset() { synchronized (ProxyHighlightsContainer.this.LOCK) { checkVersion(); if (index1 == -1 || index2 == -1) { throw new NoSuchElementException(); } return marks[index2].getMarkOffset(); } } public AttributeSet getAttributes() { synchronized (ProxyHighlightsContainer.this.LOCK) { checkVersion(); if (index1 == -1 || index2 == -1) { throw new NoSuchElementException(); } return compositeAttributes; } } private int findLowest() { int lowest = Integer.MAX_VALUE; int idx = -1; for(int i = 0; i < marks.length; i++) { if (marks[i].isFinished()) { continue; } int offset = marks[i].getMarkOffset(); if (offset < lowest) { lowest = offset; idx = i; } } return idx; } private AttributeSet findAttributes() { ArrayList list = new ArrayList(); for(int i = 0; i < marks.length; i++) { if (marks[i].getPreviousMarkAttributes() != null) { list.add(marks[i].getPreviousMarkAttributes()); } } if (!list.isEmpty()) { return AttributesUtilities.createComposite(list.toArray(new AttributeSet[list.size()])); } else { return null; } } private void checkVersion() { if (this.version != ProxyHighlightsContainer.this.version) { throw new ConcurrentModificationException(); } } } // End of ProxySeq class /* package */ static final class Sequence2Marks { private HighlightsSequence seq; private int startOffset; private int endOffset; private boolean hasNext = false; private boolean useStartOffset = true; private boolean finished = true; private int lastEndOffset = -1; private int previousMarkOffset = -1; private AttributeSet previousMarkAttributes = null; public Sequence2Marks(HighlightsSequence seq, int startOffset, int endOffset) { this.seq = seq; this.startOffset = startOffset; this.endOffset = endOffset; } public boolean isFinished() { return finished; } public boolean moveNext() { if (!useStartOffset || hasNext) { previousMarkOffset = getMarkOffset(); previousMarkAttributes = getMarkAttributes(); } if (useStartOffset) { // Move to the next highlighted area while(true == (hasNext = seq.moveNext())) { if (seq.getEndOffset() > startOffset) { break; } } if (hasNext && seq.getStartOffset() > endOffset) { hasNext = false; } if (hasNext) { if (lastEndOffset != -1 && lastEndOffset < seq.getStartOffset()) { useStartOffset = false; } else { lastEndOffset = seq.getEndOffset(); } } else { if (lastEndOffset != -1) { useStartOffset = false; } } } else { if (hasNext) { lastEndOffset = seq.getEndOffset(); } useStartOffset = true; } finished = useStartOffset && !hasNext; return !finished; } public int getMarkOffset() { if (finished) { throw new NoSuchElementException(); } return useStartOffset ? Math.max(startOffset, seq.getStartOffset()) : Math.min(endOffset, lastEndOffset); } public AttributeSet getMarkAttributes() { if (finished) { throw new NoSuchElementException(); } return useStartOffset ? seq.getAttributes() : null; } public int getPreviousMarkOffset() { return previousMarkOffset; } public AttributeSet getPreviousMarkAttributes() { return previousMarkAttributes; } } // End of Sequence2Marks class } File [added]: SyntaxHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/SyntaxHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 434 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.List; import java.util.NoSuchElementException; import java.util.WeakHashMap; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.api.lexer.Language; import org.netbeans.api.lexer.LanguagePath; import org.netbeans.api.lexer.TokenChange; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenHierarchyEvent; import org.netbeans.api.lexer.TokenHierarchyListener; import org.netbeans.api.lexer.TokenId; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.openide.util.Lookup; import org.openide.util.WeakListeners; /** * The syntax coloring layer. * * @author Vita Stejskal */ public final class SyntaxHighlighting extends AbstractHighlightsContainer implements TokenHierarchyListener { public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.SyntaxHighlighting"; //NOI18N private static WeakHashMap attribsCache = new WeakHashMap(); private final Document document; private final String mimeType; private TokenHierarchy hierarchy = null; private long version = 0; /** Creates a new instance of SyntaxHighlighting */ public SyntaxHighlighting(Document document) { this.document = document; this.mimeType = (String) document.getProperty("mimeType"); //NOI18N } public HighlightsSequence getHighlights(int startOffset, int endOffset) { synchronized (this) { if (hierarchy == null) { hierarchy = TokenHierarchy.get(document); if (hierarchy != null) { hierarchy.addTokenHierarchyListener(WeakListeners.create(TokenHierarchyListener.class, this, hierarchy)); } } if (hierarchy != null) { return new HSImpl(version, hierarchy, startOffset, endOffset); } else { return HighlightsSequence.EMPTY; } } } // ---------------------------------------------------------------------- // TokenHierarchyListener implementation // ---------------------------------------------------------------------- public void tokenHierarchyChanged(TokenHierarchyEvent evt) { synchronized (this) { version++; } TokenChange tc = evt.tokenChange(); int addedLenght = 0; int removedLength = 0; if (tc.addedTokenCount() > 0) { addedLenght = getTokensLength(tc.currentTokenSequence(), tc.index(), tc.addedTokenCount()); } if (tc.removedTokenCount() > 0) { removedLength = getTokensLength(tc.removedTokenSequence(), 0, tc.removedTokenCount()); } int changeStart = tc.offset(); int changeEnd; if (addedLenght == 0 && removedLength == 0) { // The real length couldn't be computed for some reason changeEnd = Integer.MAX_VALUE; } else { changeEnd = changeStart + Math.max(addedLenght, removedLength); } fireHighlightsChange(changeStart, changeEnd); } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private int getTokensLength(TokenSequence seq, int startIdx, int tokenCount) { assert startIdx >= 0 && startIdx < seq.tokenCount() : "Invalid startIdx: " + startIdx + ", sequence lenght: " + seq.tokenCount(); assert tokenCount > 0 && startIdx + tokenCount <= seq.tokenCount() : "Invalid tokenCount: " + tokenCount + ", startIdx: " + startIdx + ", sequence lenght: " + seq.tokenCount(); int startOffset = -1; int endOffset = -1; if (seq.moveIndex(startIdx)) { startOffset = seq.offset(); } if (seq.moveIndex(startIdx + tokenCount - 1)) { endOffset = seq.offset() + seq.token().length(); } return startOffset == -1 || endOffset == -1 ? 0 : endOffset - startOffset; } private final class HSImpl implements HighlightsSequence { private static final int S_NORMAL = 1; private static final int S_EMBEDDED_HEAD = 2; private static final int S_EMBEDDED_TAIL = 3; private static final int S_DONE = 4; private long version; private TokenHierarchy scanner; private List> sequences; private int startOffset; private int endOffset; private int state; public HSImpl(long version, TokenHierarchy scanner, int startOffset, int endOffset) { this.version = version; this.scanner = scanner; this.startOffset = startOffset; this.endOffset = endOffset; this.sequences = null; } public boolean moveNext() { checkVersion(); if (sequences == null) { // initialize TokenSequence seq = scanner.tokenSequence().subSequence(startOffset, endOffset); sequences = new ArrayList>(); sequences.add(seq); state = S_NORMAL; } switch (state) { case S_NORMAL: // The current token is a normal one state = moveTheSequence(); break; case S_EMBEDDED_HEAD: // The current token contains embedded language and we have processed it's head TokenSequence seq = sequences.get(sequences.size() - 1); if (seq.moveFirst()) { state = S_NORMAL; } else { throw new IllegalStateException("Invalid state"); } break; case S_EMBEDDED_TAIL: // The current token contains embedded language and we have processed it's tail sequences.remove(sequences.size() - 1); state = moveTheSequence(); break; case S_DONE: // We have gone through all the tokens in all sequences break; default: throw new IllegalStateException("Invalid state: " + state); } if (state == S_NORMAL) { // We have moved to the next normal token, so look what it is TokenSequence seq = sequences.get(sequences.size() - 1); TokenSequence embeddedSeq = seq.embedded(); while (embeddedSeq != null && embeddedSeq.moveFirst()) { sequences.add(sequences.size(), embeddedSeq); if (embeddedSeq.offset() > seq.offset()) { state = S_EMBEDDED_HEAD; break; } else { seq = embeddedSeq; embeddedSeq = seq.embedded(); } } } else if (state == S_DONE) { attribsCache.clear(); } return state != S_DONE; } public int getStartOffset() { checkVersion(); if (sequences == null) { throw new NoSuchElementException("Call moveNext() first."); } switch (state) { case S_NORMAL: { TokenSequence seq = sequences.get(sequences.size() - 1); return seq.offset(); } case S_EMBEDDED_HEAD: { TokenSequence embeddingSeq = sequences.get(sequences.size() - 2); return embeddingSeq.offset(); } case S_EMBEDDED_TAIL: { TokenSequence seq = sequences.get(sequences.size() - 1); if (seq.moveLast()) { return seq.offset() + seq.token().length(); } else { throw new IllegalStateException("Invalid state"); } } case S_DONE: throw new NoSuchElementException(); default: throw new IllegalStateException("Invalid state: " + state); } } public int getEndOffset() { checkVersion(); if (sequences == null) { throw new NoSuchElementException("Call moveNext() first."); } switch (state) { case S_NORMAL: { TokenSequence seq = sequences.get(sequences.size() - 1); return seq.offset() + seq.token().length(); } case S_EMBEDDED_HEAD: { TokenSequence seq = sequences.get(sequences.size() - 1); if (seq.moveFirst()) { return seq.offset(); } else { TokenSequence embeddingSeq = sequences.get(sequences.size() - 2); return embeddingSeq.offset() + embeddingSeq.token().length(); } } case S_EMBEDDED_TAIL: TokenSequence embeddingSeq = sequences.get(sequences.size() - 2); return embeddingSeq.offset() + embeddingSeq.token().length(); case S_DONE: throw new NoSuchElementException(); default: throw new IllegalStateException("Invalid state: " + state); } } public AttributeSet getAttributes() { checkVersion(); if (sequences == null) { throw new NoSuchElementException("Call moveNext() first."); } switch (state) { case S_NORMAL: return findAttribs(sequences.size() - 1); case S_EMBEDDED_HEAD: case S_EMBEDDED_TAIL: return findAttribs(sequences.size() - 2); case S_DONE: throw new NoSuchElementException(); default: throw new IllegalStateException("Invalid state: " + state); } } private AttributeSet findAttribs(int seqIdx) { TokenSequence seq = sequences.get(seqIdx); TokenId tokenId = seq.token().id(); AttributeSet tokenAttribs = findTokenAttribs(tokenId, seq.languagePath()); if (seqIdx > 0) { AttributeSet embeddingTokenAttribs = findAttribs(seqIdx - 1); return AttributesUtilities.createComposite( tokenAttribs, embeddingTokenAttribs); } else { return tokenAttribs; } } private AttributeSet findTokenAttribs(TokenId tokenId, LanguagePath lang) { MimePath mimePath = languagePathToMimePathHack(lang); Lookup lookup = MimeLookup.getLookup(mimePath); FontColorSettings fcs = lookup.lookup(FontColorSettings.class); AttributeSet attribs = findFontAndColors(fcs, tokenId, lang.innerLanguage()); return attribs != null ? attribs : SimpleAttributeSet.EMPTY; } // XXX: This hack is here to make sure that preview panels in Tools-Options // work. Currently there is no way how to force a particular JTextComponent // to use a particular MimeLookup. They all use MimeLookup common for all components // and for the mime path of things displayed in that component. The preview panels // however need special MimeLookup that loads colorings from a special profile // (i.e. not the currently active coloring profile, which is used normally by // all the other components). // // The hack is that Tools-Options modifies mime type of the document loaded // in the preview panel and prepend 'textXXXX_' at the beginning. The normal // MimeLookup for this mime type and any mime path derived from this mime type // is empty. The editor/settings/storage however provides a special handling // for these 'test' mime paths and bridge them to the MimeLookup that you would // normally get for the mime path without the 'testXXXX_' at the beginning, plus // they supply special colorings from the profile called 'testXXXX'. This way // the preview panels can have different colorings from the rest of the IDE. // // This is obviously very fragile and not fully transparent for clients as // you can see here. We need a better solution for that. Generally it should // be posible to ask somewhere for a component-specific MimeLookup. This would // normally be a standard MimeLookup as you know it, but in special cases it // could be modified by the client code that created the component - e.g. Tools-Options // panel. private MimePath languagePathToMimePathHack(LanguagePath languagePath) { if (languagePath.size() == 1) { return MimePath.parse(mimeType); } else if (languagePath.size() > 1) { return MimePath.parse(mimeType + "/" + languagePath.subPath(1).mimePath()); //NOI18N } else { throw new IllegalStateException("LanguagePath should not be empty."); //NOI18N } } private AttributeSet findFontAndColors(FontColorSettings fcs, TokenId tokenId, Language lang) { // First try the token's name String name = tokenId.name(); AttributeSet attribs = fcs.getTokenFontColors(name); // Then try the primary category if (attribs == null) { String primary = tokenId.primaryCategory(); if (primary != null) { attribs = fcs.getTokenFontColors(primary); } } // Then try all the other categories if (attribs == null) { @SuppressWarnings("unchecked") List categories = ((Language)lang).nonPrimaryTokenCategories(tokenId); for(String c : categories) { attribs = fcs.getTokenFontColors(c); if (attribs != null) { break; } } } return attribs; } private int moveTheSequence() { TokenSequence seq = sequences.get(sequences.size() - 1); if (seq.moveNext()) { return S_NORMAL; } else { if (sequences.size() > 1) { TokenSequence embeddingSeq = sequences.get(sequences.size() - 2); if (seq.moveLast()) { if ((seq.offset() + seq.token().length()) < (embeddingSeq.offset() + embeddingSeq.token().length())) { return S_EMBEDDED_TAIL; } else { sequences.remove(sequences.size() - 1); return moveTheSequence(); } } else { throw new IllegalStateException("Invalid state"); } } else { sequences.clear(); return S_DONE; } } } private void checkVersion() { synchronized (SyntaxHighlighting.this) { if (this.version != SyntaxHighlighting.this.version) { throw new ConcurrentModificationException(); } } } } // End of HSImpl class } File [added]: TextSearchHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/TextSearchHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 135 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.modules.editor.lib2.search.EditorFindSupport; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; import org.openide.util.WeakListeners; /** * * @author vita */ public class TextSearchHighlighting extends AbstractHighlightsContainer implements PropertyChangeListener, HighlightsChangeListener { private static final Logger LOG = Logger.getLogger(TextSearchHighlighting.class.getName()); public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.TextSearchHighlighting"; //NOI18N private JTextComponent component; private Document document; private OffsetsBag bag; /** Creates a new instance of TextSearchHighlighting */ public TextSearchHighlighting(JTextComponent component) { this.component = component; this.document = component.getDocument(); this.bag = new OffsetsBag(document); this.bag.addHighlightsChangeListener(this); EditorFindSupport.getInstance().addPropertyChangeListener( WeakListeners.propertyChange(this, EditorFindSupport.getInstance()) ); fillInTheBag(); } public HighlightsSequence getHighlights(int startOffset, int endOffset) { return bag.getHighlights(startOffset, endOffset); } public void highlightChanged(HighlightsChangeEvent event) { fireHighlightsChange(event.getStartOffset(), event.getEndOffset()); } public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == null || EditorFindSupport.FIND_WHAT.equals(evt.getPropertyName()) || EditorFindSupport.FIND_HIGHLIGHT_SEARCH.equals(evt.getPropertyName())) { fillInTheBag(); } } private void fillInTheBag() { document.render(new Runnable() { public void run() { OffsetsBag newBag = new OffsetsBag(document); if (LOG.isLoggable(Level.FINE)) { LOG.fine("TSH: filling the bag; enabled = " + isEnabled()); } if (isEnabled()) { try { int [] blocks = EditorFindSupport.getInstance().getBlocks( new int [] {-1, -1}, document, 0, document.getLength()); assert blocks.length % 2 == 0 : "Wrong number of block offsets"; AttributeSet attribs = getAttribs(); for (int i = 0; i < blocks.length / 2; i++) { newBag.addHighlight(blocks[2 * i], blocks[2 * i + 1], attribs); } } catch (BadLocationException e) { LOG.log(Level.WARNING, e.getMessage(), e); } } bag.setHighlights(newBag); } }); } private boolean isEnabled() { Object prop = EditorFindSupport.getInstance().getFindProperty( EditorFindSupport.FIND_HIGHLIGHT_SEARCH); return (prop instanceof Boolean) && ((Boolean) prop).booleanValue(); } private AttributeSet getAttribs() { FontColorSettings fcs = MimeLookup.getLookup( MimePath.parse(getMimeType())).lookup(FontColorSettings.class); AttributeSet attribs = fcs.getFontColors(FontColorNames.HIGHLIGHT_SEARCH_COLORING); return attribs == null ? SimpleAttributeSet.EMPTY : attribs; } private String getMimeType() { return component.getUI().getEditorKit(component).getContentType(); } } File [added]: TextSelectionHighlighting.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/highlighting/TextSelectionHighlighting.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 187 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.logging.Logger; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.AttributeSet; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; /** * The layer for highlighting a caret row. * * @author Vita Stejskal */ public class TextSelectionHighlighting extends AbstractHighlightsContainer implements ChangeListener { private static final Logger LOG = Logger.getLogger(TextSelectionHighlighting.class.getName()); public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.TextSelectionHighlighting"; //NOI18N private JTextComponent component; private int selectionStartOffset = -1; private int selectionEndOffset; /** Creates a new instance of CaretSelectionLayer */ public TextSelectionHighlighting(JTextComponent component) { this.component = component; int[] currentSelection = getCurrentSelection(); if (currentSelection != null) { selectionStartOffset = currentSelection[0]; selectionEndOffset = currentSelection[1]; } this.component.getCaret().addChangeListener(this); } public HighlightsSequence getHighlights(int startOffset, int endOffset) { if (selectionStartOffset >= 0 && selectionEndOffset >= 0 && ( (startOffset <= selectionStartOffset && endOffset > selectionStartOffset) || (endOffset >= selectionEndOffset && startOffset < selectionEndOffset))) { return new SimpleHighlightsSequence(selectionStartOffset, selectionEndOffset, getAttribs()); } else { return new SimpleHighlightsSequence(); } } public void stateChanged(ChangeEvent e) { int changeStartOffset = -1; // -1 means invalid value int changeEndOffset = 0; int[] currentSelection = getCurrentSelection(); if (currentSelection == null) { if (selectionStartOffset != -1) { // prev selection existed changeStartOffset = selectionStartOffset; changeEndOffset = selectionEndOffset; } selectionStartOffset = -1; // no longer valid selection selectionEndOffset = -1; } else { if (selectionStartOffset == -1) { // no current selection changeStartOffset = currentSelection[0]; changeEndOffset = currentSelection[1]; selectionStartOffset = currentSelection[0]; selectionEndOffset = currentSelection[1]; } else if ( currentSelection[0] != selectionStartOffset || currentSelection[1] != selectionEndOffset) { changeStartOffset = currentSelection[0] < selectionStartOffset ? currentSelection[0] : selectionStartOffset; changeEndOffset = currentSelection[1] > selectionEndOffset ? currentSelection[1] : selectionEndOffset; selectionStartOffset = currentSelection[0]; selectionEndOffset = currentSelection[1]; } } if (changeStartOffset != -1) { fireHighlightsChange(changeStartOffset, changeEndOffset); } } private int[] getCurrentSelection() { int selectionStartOffset = component.getSelectionStart(); int selectionEndOffset = component.getSelectionEnd(); if (selectionStartOffset < selectionEndOffset) { return new int[] { selectionStartOffset, selectionEndOffset, }; } return null; } private AttributeSet getAttribs() { FontColorSettings fcs = MimeLookup.getLookup( MimePath.parse(getMimeType())).lookup(FontColorSettings.class); AttributeSet attribs = fcs.getFontColors(FontColorNames.SELECTION_COLORING); if (attribs == null) { attribs = SimpleAttributeSet.EMPTY; } else { attribs = AttributesUtilities.createImmutable( attribs, AttributesUtilities.createImmutable(ATTR_EXTENDS_EMPTY_LINE, Boolean.TRUE) ); } return attribs; } private String getMimeType() { return component.getUI().getEditorKit(component).getContentType(); } private static final class SimpleHighlightsSequence implements HighlightsSequence { private int startOffset; private int endOffset; private AttributeSet attribs; private boolean end = false; public SimpleHighlightsSequence() { end = true; } public SimpleHighlightsSequence(int startOffset, int endOffset, AttributeSet attribs) { this.startOffset = startOffset; this.endOffset = endOffset; this.attribs = attribs; } public boolean moveNext() { if (!end) { end = true; return true; } else { return false; } } public int getStartOffset() { return startOffset; } public int getEndOffset() { return endOffset; } public AttributeSet getAttributes() { return attribs; } } // End of SimpleHighlightsSequence } Directory: /editor/lib2/src/org/netbeans/modules/editor/lib2/resources/ ======================================================================= File [added]: layer.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/resources/layer.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 27 --------------- Directory: /editor/lib2/src/org/netbeans/modules/editor/lib2/search/ ==================================================================== File [added]: Bundle.properties Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/search/Bundle.properties?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 79 --------------- # The contents of this file are subject to the terms of the Common Development # and Distribution License (the License). You may not use this file except in # compliance with the License. # # You can obtain a copy of the License at http://www.netbeans.org/cddl.html # or http://www.netbeans.org/cddl.txt. # # When distributing Covered Code, include this CDDL Header Notice in each file # and include the License file at http://www.netbeans.org/cddl.txt. # If applicable, add the following below the CDDL Header, with the fields # enclosed by brackets [] replaced by your own identifying information: # "Portions Copyrighted [year] [name of copyright owner]" # # The Original Software is NetBeans. The Initial Developer of the Original # Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun # Microsystems, Inc. All Rights Reserved. #FindSupport related stuff find-found=found at find-not-found=not found find-wrap-start=End of the document reached. Continuing search from beginning. find-wrap-end=Beginning of the document reached. Continuing search from end. find-block-wrap-start=End of the selection reached. Continuing search from beginning. find-block-wrap-end=Beginning of the selection reached. Continuing search from end. find-items-replaced={0} of {1} items replaced pattern-error-dialog-title=Regular Expression Error #FindDialogPanel find-what=Find What: find-what-mnemonic=F ACSD_find-what= find-replace-with=Replace With: find-replace-with-mnemonic=l ACSD_find-replace-with= find-highlight-search=\ Highlight Results find-highlight-search-mnemonic=t find-highlight-search-tooltip=Highlights all occurrences of the searched text. find-inc-search=\ Incremental Search find-inc-search-mnemonic=I find-inc-search-tooltip=Tries to find text as you type. find-match-case=\ Match Case find-match-case-mnemonic=M find-match-case-tooltip=Search the document only for text with the same capitalization. find-smart-case=\ Smart Case find-smart-case-mnemonic=S find-smart-case-tooltip=Matches case if at least one character in the searched text is uppercase. find-whole-words=\ Whole Words find-whole-words-mnemonic=W find-whole-words-tooltip=Matches the searched text only to whole words in the document. find-backward-search=\ Search Backwards find-backward-search-mnemonic=B find-backward-search-tooltip=Searches backwards from the current cursor position. find-wrap-search=\ Wrap Around find-wrap-search-mnemonic=p find-wrap-search-tooltip=Continues search from the beginning if end of the document is reached. find-reg-exp=\ Regular Expressions find-reg-exp-mnemonic=E find-reg-exp-tooltip=Uses regular expressions to search the document. find-block-search=\ Search Selection find-block-search-mnemonic=c find-block-search-tooltip=Searches for occurrences in a selected block of text only. ACSD_find= #FindDialogSupport find-title=Find replace-title=Replace find-button-find=Find find-button-find-mnemonic=D find-button-replace=Replace find-button-replace-mnemonic=R find-button-replace-all=Replace All find-button-replace-all-mnemonic=A find-button-cancel=Close ACSD_find-button-find= ACSD_find-button-replace= ACSD_find-button-replace-all= ACSD_find-button-cancel= File [added]: DocumentFinder.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/search/DocumentFinder.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1176 ----------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.search; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.editor.lib2.DocUtils; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.NbBundle; /** * * @author Martin Roskanin */ public class DocumentFinder { private static FalseBlocksFinder falseBlocksFinder; private static FalseFinder falseFinder; private static WholeWordsBlocksFinder wholeWordsBlocksFinder; private static RegExpBlocksFinder regExpBlocksFinder; private static StringBlocksFinder stringBlocksFinder; private static WholeWordsBwdFinder wholeWordsBwdFinder; private static WholeWordsFwdFinder wholeWordsFwdFinder; private static RegExpBwdFinder regExpBwdFinder; private static RegExpFwdFinder regExpFwdFinder; private static StringBwdFinder stringBwdFinder; private static StringFwdFinder stringFwdFinder; /** Creates a new instance of DocumentFinder */ private DocumentFinder() { } private static DocFinder getFinder(Document doc, Map searchProps, boolean oppositeDir, boolean blocksFinder){ String text = (String)searchProps.get(EditorFindSupport.FIND_WHAT); if (text == null || text.length() == 0) { if (blocksFinder) { if (falseBlocksFinder == null){ falseBlocksFinder = new FalseBlocksFinder(); } return falseBlocksFinder; } else { if (falseFinder == null){ falseFinder = new FalseFinder(); } return falseFinder; } } Boolean b = (Boolean)searchProps.get(EditorFindSupport.FIND_BACKWARD_SEARCH); boolean bwdSearch = (b != null && b.booleanValue()); if (oppositeDir) { // negate for opposite direction search bwdSearch = !bwdSearch; } b = (Boolean)searchProps.get(EditorFindSupport.FIND_MATCH_CASE); boolean matchCase = (b != null && b.booleanValue()); b = (Boolean)searchProps.get(EditorFindSupport.FIND_SMART_CASE); boolean smartCase = (b != null && b.booleanValue()); b = (Boolean)searchProps.get(EditorFindSupport.FIND_WHOLE_WORDS); boolean wholeWords = (b != null && b.booleanValue()); if (smartCase && !matchCase) { int cnt = text.length(); for (int i = 0; i < cnt; i++) { if (Character.isUpperCase(text.charAt(i))) { matchCase = true; } } } b = (Boolean) searchProps.get(EditorFindSupport.FIND_REG_EXP); boolean regExpSearch = (b!=null && b.booleanValue()); Pattern pattern = null; if (regExpSearch){ try{ pattern = PatternCache.getPattern(text, matchCase); if (pattern == null){ pattern = (matchCase) ? Pattern.compile(text, Pattern.MULTILINE) : Pattern.compile(text, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // NOI18N PatternCache.putPattern(text, matchCase, pattern); } }catch(PatternSyntaxException pse){ if (!blocksFinder){ NotifyDescriptor msg = new NotifyDescriptor.Message( pse.getDescription(), NotifyDescriptor.ERROR_MESSAGE); msg.setTitle(NbBundle.getBundle(DocumentFinder.class).getString("pattern-error-dialog-title")); //NOI18N DialogDisplayer.getDefault().notify(msg); } PatternCache.putPattern(text, matchCase, null); return null; } }else{ PatternCache.clear(); } if (blocksFinder) { if (wholeWords && !regExpSearch) { if (wholeWordsBlocksFinder == null){ wholeWordsBlocksFinder = new WholeWordsBlocksFinder(); } wholeWordsBlocksFinder.setParams(doc, text, matchCase); return wholeWordsBlocksFinder; } else { if (regExpSearch){ if (regExpBlocksFinder == null){ regExpBlocksFinder = new RegExpBlocksFinder(); } regExpBlocksFinder.setParams(pattern, matchCase); return regExpBlocksFinder; }else{ if (stringBlocksFinder == null){ stringBlocksFinder = new StringBlocksFinder(); } stringBlocksFinder.setParams(text, matchCase); return stringBlocksFinder; } } } else { if (wholeWords && !regExpSearch) { if (bwdSearch) { if (wholeWordsBwdFinder == null){ wholeWordsBwdFinder = new WholeWordsBwdFinder(); } wholeWordsBwdFinder.setParams(doc, text, matchCase); return wholeWordsBwdFinder; } else { if (wholeWordsFwdFinder == null){ wholeWordsFwdFinder = new WholeWordsFwdFinder(); } wholeWordsFwdFinder.setParams(doc, text, matchCase); return wholeWordsFwdFinder; } } else { if (regExpSearch){ if (bwdSearch) { if (regExpBwdFinder == null){ regExpBwdFinder = new RegExpBwdFinder(); } regExpBwdFinder.setParams(pattern, matchCase); return regExpBwdFinder; } else { if (regExpFwdFinder == null){ regExpFwdFinder = new RegExpFwdFinder(); } regExpFwdFinder.setParams(pattern, matchCase); return regExpFwdFinder; } }else{ if (bwdSearch) { if (stringBwdFinder == null){ stringBwdFinder = new StringBwdFinder(); } stringBwdFinder.setParams(text, matchCase); return stringBwdFinder; } else { if (stringFwdFinder == null){ stringFwdFinder = new StringFwdFinder(); } stringFwdFinder.setParams(text, matchCase); return stringFwdFinder; } } } } } private static FindReplaceResult findReplaceImpl(String replaceText, Document doc, int startOffset, int endOffset, Map props, boolean oppositeDir) throws BadLocationException{ int ret[] = new int[2]; if (endOffset == -1){ endOffset = doc.getLength(); } if (startOffset>endOffset){ int temp = startOffset; startOffset = endOffset; endOffset = temp; } DocFinder finder = getFinder(doc, props, oppositeDir, false); if (finder == null){ return null; } finder.reset(); CharSequence cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset); if (cs==null) return null; int findRet = finder.find(startOffset, cs); if (!finder.isFound()){ ret[0] = -1; return new FindReplaceResult(ret, replaceText); } ret[0] = startOffset + findRet; if (finder instanceof StringFinder){ int length = ((StringFinder)finder).getFoundLength(); ret[1] = ret [0] + length; } if (finder instanceof RegExpFinder){ Matcher matcher = ((RegExpFinder)finder).getMatcher(); if (matcher != null && replaceText != null){ CharSequence foundString = cs.subSequence(ret[0]-startOffset, ret[1]-startOffset); matcher.reset(foundString); if (matcher.find()){ try{ replaceText = matcher.replaceFirst(replaceText); }catch(IndexOutOfBoundsException ioobe){ NotifyDescriptor msg = new NotifyDescriptor.Message( ioobe.getLocalizedMessage(), NotifyDescriptor.ERROR_MESSAGE); msg.setTitle(NbBundle.getBundle(DocumentFinder.class).getString("pattern-error-dialog-title")); //NOI18N DialogDisplayer.getDefault().notify(msg); return null; } } } } return new FindReplaceResult(ret, replaceText); } /** * Finds in document * * @param doc document where to find * @param startOffset offset in the document where the search will start * @param endOffset offset where the search will end with reporting * that nothing was found. * @param props find properties */ public static int[] find(Document doc, int startOffset, int endOffset, Map props, boolean oppositeDir) throws BadLocationException{ FindReplaceResult result = findReplaceImpl(null, doc, startOffset, endOffset, props, oppositeDir); if (result == null){ return null; } return result.getFoundPositions(); } public static int[] findBlocks(Document doc, int startOffset, int endOffset, Map props, int blocks[]) throws BadLocationException{ BlocksFinder finder =(BlocksFinder) getFinder(doc, props, false, true); if (finder == null){ return blocks; } finder.reset(); finder.setBlocks(blocks); CharSequence cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset); if (cs==null){ return null; } finder.find(startOffset, cs); int ret [] = finder.getBlocks(); return ret; } /** * Finds the searching string and substitute replace expression in case of * regexp backreferences. * @return FindReplaceResult, that contains positions of found string and substituted replace expression */ public static FindReplaceResult findReplaceResult(String replaceString, Document doc, int startOffset, int endOffset, Map props, boolean oppositeDir) throws BadLocationException{ return findReplaceImpl(replaceString, doc, startOffset, endOffset, props, oppositeDir); } private interface DocFinder{ public int find(int initOffset, CharSequence data); public boolean isFound(); public void reset(); } private static final class FalseBlocksFinder extends AbstractBlocksFinder { public int find(int initOffset, CharSequence data) { return -1; } } /** Request non-existent position immediately */ private static class FalseFinder extends AbstractFinder implements StringFinder { public int find(int initOffset, CharSequence data) { return -1; } public int getFoundLength() { return 0; } } private static abstract class AbstractBlocksFinder extends AbstractFinder implements BlocksFinder { private static int[] EMPTY_INT_ARRAY = new int[0]; private int[] blocks = EMPTY_INT_ARRAY; private int blocksInd; private boolean closed; public void reset() { blocksInd = 0; closed = false; } public int[] getBlocks() { if (!closed) { // not closed yet closeBlocks(); closed = true; } return blocks; } public void setBlocks(int[] blocks) { this.blocks = blocks; closed = false; } protected void addBlock(int blkStartPos, int blkEndPos) { if (blocksInd == blocks.length) { int[] dbl = new int[blocks.length * 2]; System.arraycopy(blocks, 0, dbl, 0, blocks.length); blocks = dbl; } blocks[blocksInd++] = blkStartPos; blocks[blocksInd++] = blkEndPos; } /** Insert closing sequence [-1, -1] */ protected void closeBlocks() { addBlock(-1, -1); } public String debugBlocks() { StringBuffer buf = new StringBuffer(); int ind = 0; while (blocks[ind] != -1) { buf.append((ind/2 + 1) + ": [" + blocks[ind] + ", " + blocks[ind + 1] + "]\n"); // NOI18N ind+= 2; } return buf.toString(); } } /** Finder that constructs [begin-pos, end-pos] blocks. * This is useful for highlight-search draw layer. * The block-finders are always forward-search finders. */ private interface BlocksFinder extends DocFinder { /** Set the array into which the finder puts * the position blocks. If the length of array is not sufficient * the finder extends the array. The last block is set to [-1, -1]. */ public void setBlocks(int[] blocks); /** Get the array filled with position blocks. It is either * original array passed to setBlocks() or the new array * if the finder extended the array. */ public int[] getBlocks(); } /** Abstract finder implementation. The only find() * method must be redefined. */ private static abstract class AbstractFinder implements DocFinder { /** Was the string found? */ protected boolean found; /** Was the string found? */ public final boolean isFound() { return found; } /** Reset the finder */ public void reset() { found = false; } } /** Finder that looks for some search expression expressed by string. * It can be either simple string * or some form of regular expression expressed by string. */ private interface StringFinder extends DocFinder { /** Get the length of the found string. This is useful * for regular expressions, because the length of the regular * expression can be different than the length of the string * that matched the expression. */ public int getFoundLength(); } /** String forward finder that finds whole words only * and that creates position blocks. * There are some speed optimizations attempted. */ private static final class WholeWordsBlocksFinder extends AbstractBlocksFinder { char chars[]; int stringInd; boolean matchCase; boolean insideWord; boolean firstCharWordPart; boolean wordFound; Document doc; public WholeWordsBlocksFinder() { } public void setParams(Document doc, String s, boolean matchCase){ this.matchCase = matchCase; this.doc = doc; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); firstCharWordPart = DocUtils.isIdentifierPart(doc, chars[0]); } public void reset() { super.reset(); insideWord = false; wordFound = false; stringInd = 0; } public int find(int initOffset, CharSequence data) { int offset = 0; int limitPos = data.length(); int limitOffset = limitPos - 1; while (offset >= 0 && offset < limitPos) { char ch = data.charAt(offset); if (!matchCase) { ch = Character.toLowerCase(ch); } // whole word already found but must verify next char if (wordFound) { if (DocUtils.isIdentifierPart(doc, ch)) { // word continues insideWord = firstCharWordPart; offset -= chars.length - 1; } else { int blkEnd = initOffset + offset; addBlock(blkEnd - chars.length, blkEnd); insideWord = false; offset++; } wordFound = false; stringInd = 0; continue; } if (stringInd == 0) { // special case for first char if (ch != chars[0] || insideWord) { // first char doesn't match insideWord = DocUtils.isIdentifierPart(doc, ch); offset++; } else { // first char matches stringInd = 1; // matched and not inside word if (chars.length == 1) { if (offset == limitOffset) { int blkStart = initOffset + offset; addBlock(blkStart, blkStart + 1); } else { wordFound = true; } } offset++; } } else { // already matched at least one char if (ch == chars[stringInd]) { // matches current char stringInd++; if (stringInd == chars.length) { // found whole string if (offset == limitOffset) { int blkEnd = initOffset + 1; addBlock(blkEnd - stringInd, blkEnd); } else { wordFound = true; } } offset++; } else { // current char doesn't match, stringInd > 0 offset += 1 - stringInd; stringInd = 0; insideWord = firstCharWordPart; } } } return offset; } } /** String forward finder that creates position blocks */ private static final class StringBlocksFinder extends AbstractBlocksFinder { char chars[]; int stringInd; boolean matchCase; public StringBlocksFinder() { } public void setParams(String s, boolean matchCase){ this.matchCase = matchCase; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); } public void reset() { super.reset(); stringInd = 0; } public int find(int initOffset, CharSequence data) { int offset = 0; int endPos = data.length(); while (offset >= 0 && offset < endPos) { char ch = data.charAt(offset); if (!matchCase) { ch = Character.toLowerCase(ch); } if (ch == chars[stringInd]) { stringInd++; if (stringInd == chars.length) { int blkEnd = initOffset + offset + 1; addBlock(blkEnd - stringInd, blkEnd); stringInd = 0; } offset++; } else { offset += 1 - stringInd; stringInd = 0; } } return offset; } } private static final class WholeWordsBwdFinder extends GenericBwdFinder implements StringFinder { char chars[]; int stringInd; boolean matchCase; boolean insideWord; boolean lastCharWordPart; boolean wordFound; int endInd; Document doc; public WholeWordsBwdFinder() { } public void setParams(Document doc, String s, boolean matchCase){ this.doc = doc; this.matchCase = matchCase; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); endInd = chars.length - 1; DocUtils.isIdentifierPart(doc, chars[endInd]); } public int getFoundLength() { return chars.length; } public void reset() { super.reset(); insideWord = false; wordFound = false; stringInd = endInd; } protected int scan(char ch, boolean lastChar) { if (!matchCase) { ch = Character.toLowerCase(ch); } // whole word already found but must verify next char if (wordFound) { if (DocUtils.isIdentifierPart(doc, ch)) { // word continues wordFound = false; insideWord = lastCharWordPart; stringInd = endInd; return endInd; } else { found = true; return 1; } } if (stringInd == endInd) { // special case for last char if (ch != chars[endInd] || insideWord) { // first char doesn't match insideWord = DocUtils.isIdentifierPart(doc, ch); return -1; } else { // first char matches stringInd = endInd - 1; // matched and not inside word if (chars.length == 1) { if (lastChar) { found = true; return 0; } else { wordFound = true; return -1; } } return -1; } } else { // already matched at least one char if (ch == chars[stringInd]) { // matches current char stringInd--; if (stringInd == -1) { // found whole string if (lastChar) { found = true; return 0; } else { wordFound = true; return -1; } } return -1; // successfully matched char, go to next char } else { // current char doesn't match, stringInd > 0 int back = chars.length - 2 - stringInd; stringInd = endInd; insideWord = lastCharWordPart; return back; } } } } /** Generic forward finder that simplifies the search process. */ private static abstract class GenericFwdFinder extends AbstractFinder { public final int find(int initOffset, CharSequence chars) { int offset = 0; int limitPos = chars.length(); int limitOffset = limitPos - 1; while (offset >= 0 && offset < limitPos) { offset += scan(chars.charAt(offset), (offset == limitOffset)); if (found) { break; } } return offset; } /** This function decides if it found a desired string or not. * The function receives currently searched character and flag if it's * the last one that is searched or not. * @return if the function decides that * it found a desired string it sets found = true and returns * how many characters back the searched string begins in forward * direction (0 stands for current character). * For example if the function looks for word 'yes' and it gets * 's' as parameter it sets found = true and returns -2. * If the string is not yet found it returns how many characters it should go * in forward direction (in this case it would usually be 1). * The next searched character will be that one requested. */ protected abstract int scan(char ch, boolean lastChar); } /** Generic backward finder that simplifies the search process. */ private static abstract class GenericBwdFinder extends AbstractFinder { public final int find(int initOffset, CharSequence chars) { int offset = chars.length() - 1; int offset2; int limitPos = 0; int limitOffset = chars.length(); while (offset >= 0 && offset < limitOffset) { offset += scan(chars.charAt(offset), (offset == limitOffset)); if (found) { break; } } return offset; } /** This function decides if it found a desired string or not. * The function receives currently searched character and flag if it's * the last one that is searched or not. * @return if the function decides that * it found a desired string it sets found = true and returns * how many characters back the searched string begins in backward * direction (0 stands for current character). It is usually 0 as the * finder usually decides after the last required character but it's * not always the case e.g. for whole-words-only search it can be 1 or so. * If the string is not yet found it returns how many characters it should go * in backward direction (in this case it would usually be -1). * The next searched character will be that one requested. */ protected abstract int scan(char ch, boolean lastChar); } private static final class WholeWordsFwdFinder extends GenericFwdFinder implements StringFinder { char chars[]; int stringInd; boolean matchCase; Document doc; boolean insideWord; boolean firstCharWordPart; boolean wordFound; public WholeWordsFwdFinder() { } public void setParams(Document doc, String s, boolean matchCase){ this.doc = doc; this.matchCase = matchCase; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); firstCharWordPart = DocUtils.isIdentifierPart(doc, chars[0]); } public int getFoundLength() { return chars.length; } public void reset() { super.reset(); insideWord = false; wordFound = false; stringInd = 0; } protected int scan(char ch, boolean lastChar) { if (!matchCase) { ch = Character.toLowerCase(ch); } // whole word already found but must verify next char if (wordFound) { if (DocUtils.isIdentifierPart(doc, ch)) { // word continues wordFound = false; insideWord = firstCharWordPart; stringInd = 0; return 1 - chars.length; } else { found = true; return -chars.length; } } if (stringInd == 0) { // special case for first char if (ch != chars[0] || insideWord) { // first char doesn't match insideWord = DocUtils.isIdentifierPart(doc, ch); return 1; } else { // first char matches stringInd = 1; // matched and not inside word if (chars.length == 1) { if (lastChar) { found = true; return 0; } else { wordFound = true; return 1; } } return 1; } } else { // already matched at least one char if (ch == chars[stringInd]) { // matches current char stringInd++; if (stringInd == chars.length) { // found whole string if (lastChar) { found = true; return 1 - chars.length; // how many chars back the string starts } else { wordFound = true; return 1; } } return 1; // successfully matched char, go to next char } else { // current char doesn't match, stringInd > 0 int back = 1 - stringInd; stringInd = 0; insideWord = firstCharWordPart; return back; // go back to search from the next to first char } } } } private static class StringBwdFinder extends GenericBwdFinder implements StringFinder { char chars[]; int stringInd; boolean matchCase; int endInd; public StringBwdFinder() { } public void setParams(String s, boolean matchCase){ this.matchCase = matchCase; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); endInd = chars.length - 1; } public int getFoundLength() { return chars.length; } public void reset() { super.reset(); stringInd = endInd; } protected int scan(char ch, boolean lastChar) { if (!matchCase) { ch = Character.toLowerCase(ch); } if (ch == chars[stringInd]) { stringInd--; if (stringInd == -1) { found = true; return 0; } return -1; } else { if (stringInd == endInd) { return -1; } else { int back = chars.length - 2 - stringInd; stringInd = endInd; return back; } } } } private static final class StringFwdFinder extends GenericFwdFinder implements StringFinder { char chars[]; int stringInd; boolean matchCase; public StringFwdFinder() { } public void setParams(String s, boolean matchCase){ this.matchCase = matchCase; chars = (matchCase ? s : s.toLowerCase()).toCharArray(); } public int getFoundLength() { return chars.length; } public void reset() { super.reset(); stringInd = 0; } protected int scan(char ch, boolean lastChar) { if (!matchCase) { ch = Character.toLowerCase(ch); } if (ch == chars[stringInd]) { stringInd++; if (stringInd == chars.length) { // found whole string found = true; return 1 - stringInd; // how many chars back the string starts } return 1; // successfully matched char, go to next char } else { if (stringInd == 0) { return 1; } else { int back = 1 - stringInd; stringInd = 0; return back; } } } } private abstract static class RegExpFinder extends AbstractFinder implements StringFinder{ public abstract Matcher getMatcher(); } // ----------------- regexp ---------------------- private static class RegExpBwdFinder extends RegExpFinder{ boolean matchCase; Pattern pattern; int length = 0; Matcher matcher; public RegExpBwdFinder() { } public Matcher getMatcher(){ return matcher; } public void setParams(Pattern pattern, boolean matchCase){ this.matchCase = matchCase; this.pattern = pattern; } public int getFoundLength() { return length; } public void reset() { super.reset(); length = 0; } private int lineFind (int lineStart, int lineEnd, CharSequence chars){ matcher = pattern.matcher(chars.subSequence(lineStart, lineEnd)); int ret = -1; while (matcher.find()){ int start = matcher.start(); int end = matcher.end(); length = end - start; if (length <= 0){ found = false; return -1; } ret = start; } return ret; } public int find(int initOffset, CharSequence chars) { char ch; int charsEnd = chars.length() - 1; int lineEnd = charsEnd; int lineStart = charsEnd; for (int i = charsEnd; i>=0; i--){ ch = chars.charAt(i); if (ch == '\n' || i==0){ int retFind = lineFind (lineStart+((i==0)?0:1), lineEnd+1, chars); if (retFind!=-1){ found = true; return i + retFind + ((i==0)?0:1); } lineStart--; lineEnd = lineStart; }else{ lineStart--; } } return -1; } } private static final class RegExpFwdFinder extends RegExpFinder{ Pattern pattern; boolean matchCase; int length = 0; Matcher matcher; public RegExpFwdFinder() { } public Matcher getMatcher(){ return matcher; } public void setParams(Pattern pattern, boolean matchCase){ this.matchCase = matchCase; this.pattern = pattern; } public int getFoundLength() { return length; } public void reset() { super.reset(); length = 0; } public int find(int initOffset, CharSequence chars) { matcher = pattern.matcher(chars); if (matcher.find()){ found = true; int start = matcher.start(); int end = matcher.end(); length = end - start; if (length <= 0){ found = false; return -1; } return start; }else{ return -1; } } } /** String forward finder that creates position blocks */ private static final class RegExpBlocksFinder extends AbstractBlocksFinder { Pattern pattern; int stringInd; boolean matchCase; public RegExpBlocksFinder() { } public void setParams(Pattern pattern, boolean matchCase){ this.pattern = pattern; this.matchCase = matchCase; } public void reset() { super.reset(); stringInd = 0; } public int find(int initOffset, CharSequence data) { Matcher matcher = pattern.matcher(data); int ret = 0; while (matcher.find()){ int start = initOffset + matcher.start(); int end = initOffset + matcher.end(); addBlock(start, end); ret = start; } return ret; } } private static class PatternCache{ private static String cache_str; private static boolean cache_matchCase; private static Pattern cache_pattern; private PatternCache(){ } public static void putPattern(String str, boolean matchCase, Pattern pattern){ cache_str = str; cache_matchCase = matchCase; cache_pattern = pattern; } public static Pattern getPattern(String str, boolean matchCase){ if (str == null) return null; if (str.equals(cache_str) && matchCase == cache_matchCase){ return cache_pattern; } return null; } public static void clear(){ cache_str = null; cache_matchCase = false; cache_pattern = null; } } public static class FindReplaceResult{ private int[] positions; private String replacedString; public FindReplaceResult(int[] positions, String replacedString){ this.positions = positions; this.replacedString = replacedString; } public String getReplacedString(){ return replacedString; } public int[] getFoundPositions(){ return positions; } } } File [added]: EditorFindDialogPanel.form Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogPanel.form?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 236 ----------------

File [added]: EditorFindDialogPanel.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogPanel.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 233 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.search; import java.awt.Dimension; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.JCheckBox; import org.openide.util.NbBundle; /** * * @author Miloslav Metelka, Petr Nejedly * @version 1.0 */ public class EditorFindDialogPanel extends javax.swing.JPanel { static final long serialVersionUID =5048601763767383114L; private final ResourceBundle bundle = NbBundle.getBundle(EditorFindDialogPanel.class); /** Initializes the Form */ public EditorFindDialogPanel() { initComponents (); getAccessibleContext().setAccessibleName(bundle.getString("find-title")); // NOI18N getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_find")); // NOI18N findWhat.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_" + EditorFindSupport.FIND_WHAT)); // NOI18N replaceWith.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_" + EditorFindSupport.FIND_REPLACE_WITH)); // NOI18N // #71956 Dimension findPrefSize = findWhat.getPreferredSize(); Dimension replacePrefSize = replaceWith.getPreferredSize(); if (findPrefSize != null){ findWhat.setPreferredSize(new Dimension((int)findPrefSize.getWidth(), (int)findPrefSize.getHeight())); } if (replacePrefSize != null){ replaceWith.setPreferredSize(new Dimension((int)replacePrefSize.getWidth(), (int)replacePrefSize.getHeight())); } } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the FormEditor. */ // //GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; findWhatPanel = new javax.swing.JPanel(); findWhatLabel = new javax.swing.JLabel(); findWhat = new javax.swing.JComboBox(); replaceWithLabel = new javax.swing.JLabel(); replaceWith = new javax.swing.JComboBox(); highlightSearch = createCheckBox( EditorFindSupport.FIND_HIGHLIGHT_SEARCH, 'H' ); incSearch = createCheckBox( EditorFindSupport.FIND_INC_SEARCH, 'I' ); matchCase = createCheckBox( EditorFindSupport.FIND_MATCH_CASE, 'C' ); wholeWords = createCheckBox( EditorFindSupport.FIND_WHOLE_WORDS, 'W' ); bwdSearch = createCheckBox( EditorFindSupport.FIND_BACKWARD_SEARCH, 'B' ); wrapSearch = createCheckBox( EditorFindSupport.FIND_WRAP_SEARCH, 'p' ); regExp = createCheckBox( EditorFindSupport.FIND_REG_EXP, 'E' ); blockSearch = createCheckBox( EditorFindSupport.FIND_BLOCK_SEARCH, 'l' ); setLayout(new java.awt.GridBagLayout()); findWhatPanel.setLayout(new java.awt.GridBagLayout()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(12, 0, 0, 0); add(findWhatPanel, gridBagConstraints); findWhatLabel.setLabelFor(findWhat); findWhatLabel.setText(bundle.getString(EditorFindSupport.FIND_WHAT ) ); findWhatLabel.setDisplayedMnemonic(bundle.getString(EditorFindSupport.FIND_WHAT + "-mnemonic").charAt(0)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(0, 12, 5, 0); add(findWhatLabel, gridBagConstraints); findWhat.setEditable(true); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(0, 11, 9, 10); add(findWhat, gridBagConstraints); replaceWithLabel.setLabelFor(replaceWith); replaceWithLabel.setText(bundle.getString(EditorFindSupport.FIND_REPLACE_WITH ) ); replaceWithLabel.setDisplayedMnemonic(bundle.getString(EditorFindSupport.FIND_REPLACE_WITH + "-mnemonic").charAt(0)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(0, 12, 9, 0); add(replaceWithLabel, gridBagConstraints); replaceWith.setEditable(true); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(0, 11, 9, 10); add(replaceWith, gridBagConstraints); highlightSearch.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 6; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(11, 11, 11, 0); add(highlightSearch, gridBagConstraints); incSearch.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 6; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(11, 11, 11, 10); add(incSearch, gridBagConstraints); matchCase.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 3; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 11, 0, 0); add(matchCase, gridBagConstraints); wholeWords.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 4; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 0); add(wholeWords, gridBagConstraints); bwdSearch.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 5; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 10); add(bwdSearch, gridBagConstraints); wrapSearch.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 3; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 11, 0, 10); add(wrapSearch, gridBagConstraints); regExp.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 5; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 0); add(regExp, gridBagConstraints); blockSearch.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 4; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 10); add(blockSearch, gridBagConstraints); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables protected javax.swing.JCheckBox blockSearch; protected javax.swing.JCheckBox bwdSearch; protected javax.swing.JComboBox findWhat; protected javax.swing.JLabel findWhatLabel; protected javax.swing.JPanel findWhatPanel; protected javax.swing.JCheckBox highlightSearch; protected javax.swing.JCheckBox incSearch; protected javax.swing.JCheckBox matchCase; protected javax.swing.JCheckBox regExp; protected javax.swing.JComboBox replaceWith; protected javax.swing.JLabel replaceWithLabel; protected javax.swing.JCheckBox wholeWords; protected javax.swing.JCheckBox wrapSearch; // End of variables declaration//GEN-END:variables private JCheckBox createCheckBox( String key, char mnemonic ) { JCheckBox box = new JCheckBox( bundle.getString( key ) ); box.setToolTipText( bundle.getString( key + "-tooltip" ) ); char mnemonicChar; try { mnemonicChar = bundle.getString( key + "-mnemonic").charAt(0); } catch (MissingResourceException e) { mnemonicChar = mnemonic; } box.setMnemonic(mnemonicChar); // NOI18N return box; } } File [added]: EditorFindDialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/modules/editor/lib2/search/EditorFindDialogSupport.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 756 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.search; import java.awt.Dialog; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Collections; import java.util.ResourceBundle; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.*; import javax.swing.text.JTextComponent; import javax.swing.text.BadLocationException; import javax.swing.text.Position; import java.util.Iterator; import javax.swing.text.Document; import org.netbeans.modules.editor.lib2.DocumentsRegistry; import org.netbeans.modules.editor.lib2.KeyEventBlocker; import org.netbeans.modules.editor.lib2.DocUtils; import org.netbeans.modules.editor.lib2.ComponentUtils; import org.netbeans.modules.editor.lib2.DialogSupport; import org.netbeans.modules.editor.lib2.highlighting.BlockHighlighting; import org.netbeans.modules.editor.lib2.highlighting.Factory; import org.netbeans.modules.editor.lib2.search.EditorFindSupport.SPW; import org.openide.util.NbBundle; /** * Support for displaying find and replace dialogs * *

IMPORTANT: Do not subclass this class and do not use its constructor. * Always use getInstance method. * * @author Miloslav Metelka * @version 1.00 */ public class EditorFindDialogSupport extends WindowAdapter implements ActionListener { private static final Logger LOG = Logger.getLogger(EditorFindDialogSupport.class.getName()); /** This lock is used to create a barrier between showing/hiding/changing * the dialog and testing if the dialog is already shown. * it is used to make test-and-change / test-and-display actions atomic. * It covers the following four fields: findDialog, isReplaceDialog, * findPanel, findButtons */ private static Object dialogLock = new Object(); /** Whether the currently visible dialog is for replace */ private static boolean isReplaceDialog = false; /** The buttons used in the visible dialog */ private static JButton findButtons[]; private static JButton findDialogButtons[]; private static JButton replaceDialogButtons[]; /** The FindPanel used inside the visible dialog */ private static FindPanel findPanel; /** Currently visible dialog */ private static Dialog findDialog = null; private int caretPosition; private static EditorFindDialogSupport singleton = null; private static PropertyChangeListener historyChangeListener; private boolean findPerformed = false; private static int xPos = Integer.MIN_VALUE; private static int yPos = Integer.MIN_VALUE; /** Flag for determining a dialog invocation. It the dialog * is invoked by keystroke or by the menu the value is true. * If the dialog was already shown and the focus was bring to it only, * value is false - needed for fixing the issue #68021 */ private static boolean dialogInvokedViaKeystroke; public static EditorFindDialogSupport getInstance() { if (singleton == null) { singleton = new EditorFindDialogSupport(); } return singleton; } /** *

IMPORTANT: This is public only to maintain backwards compatibility * of the FindDialogSupport class. Do not use this constructor, use getInstance * method instead. */ public EditorFindDialogSupport() { } private void createFindButtons() { if (findButtons == null) { ResourceBundle bundle = NbBundle.getBundle(EditorFindDialogSupport.class); findButtons = new JButton[] { new JButton(bundle.getString("find-button-find")), // NOI18N new JButton(bundle.getString("find-button-replace")), // NOI18N new JButton(bundle.getString("find-button-replace-all")), // NOI18N new JButton(bundle.getString("find-button-cancel")) // NOI18N }; findButtons[0].setMnemonic(bundle.getString("find-button-find-mnemonic").charAt(0)); // NOI18N findButtons[1].setMnemonic(bundle.getString("find-button-replace-mnemonic").charAt(0)); // NOI18N findButtons[2].setMnemonic(bundle.getString("find-button-replace-all-mnemonic").charAt(0)); // NOI18N findButtons[0].getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_find-button-find")); // NOI18N findButtons[1].getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_find-button-replace")); // NOI18N findButtons[2].getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_find-button-replace-all")); // NOI18N findButtons[3].getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_find-button-cancel")); // NOI18N findDialogButtons = new JButton[2]; findDialogButtons[0] = findButtons[0]; findDialogButtons[1] = findButtons[3]; replaceDialogButtons = new JButton[4]; replaceDialogButtons[0] = findButtons[0]; replaceDialogButtons[1] = findButtons[1]; replaceDialogButtons[2] = findButtons[2]; replaceDialogButtons[3] = findButtons[3]; } } private void createFindPanel() { if (findPanel == null) { findPanel = new FindPanel(); } } private Dialog createFindDialog(JPanel findPanel, final JButton[] buttons, final ActionListener l) { Dialog d = DialogSupport.getInstance().createDialog( isReplaceDialog ? NbBundle.getBundle(EditorFindDialogSupport.class).getString ("replace-title") : NbBundle.getBundle(EditorFindDialogSupport.class).getString ("find-title" ), // NOI18N findPanel, false, // non-modal buttons, true, // sidebuttons, 0, // defaultIndex = 0 => findButton isReplaceDialog ? 3 : 1, // cancelIndex = 3 => cancelButton l //listener ); return d; } private void showFindDialogImpl( boolean isReplace, KeyEventBlocker blocker) { dialogInvokedViaKeystroke = true; synchronized( dialogLock ) { if (findDialog != null && isReplaceDialog != isReplace ) { xPos = findDialog.getLocation().x; yPos = findDialog.getLocation().y; findDialog.dispose(); findDialog = null; } if (findDialog == null) { // create and show new dialog of required type isReplaceDialog = isReplace; createFindButtons(); createFindPanel(); findPanel.changeVisibility(isReplace); findDialog = createFindDialog( findPanel, isReplace ? replaceDialogButtons : findDialogButtons, this ); findDialog.addWindowListener( this ); ((JDialog)findDialog).getRootPane().setFocusable(false); if(xPos > Integer.MIN_VALUE){ findDialog.setLocation(xPos, yPos); } } } // end of synchronized section findDialog.pack(); findPanel.init(isReplace, blocker); findDialog.setVisible(true); findPanel.showNotify(); updateCaretPosition(); } private void updateCaretPosition() { JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { caretPosition = c.getCaret().getDot(); } } public void windowActivated(WindowEvent evt) { findPerformed = false; createFindPanel(); findPanel.initBlockSearch(); updateCaretPosition(); } public void windowDeactivated(WindowEvent evt) { Map findProps = findPanel.getFindProps(); JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { boolean blockSearch = getBooleanProp(EditorFindSupport.FIND_BLOCK_SEARCH, findProps); if (blockSearch && !findPerformed){ Integer bsStartInt = (Integer)findProps.get(EditorFindSupport.FIND_BLOCK_SEARCH_START); int bsStart = (bsStartInt == null) ? -1 : bsStartInt.intValue(); Position pos = (Position) findProps.get(EditorFindSupport.FIND_BLOCK_SEARCH_END); int bsEnd = (pos != null) ? pos.getOffset() : -1; if (bsStart >=0 && bsEnd > 0){ c.select(bsStart, bsEnd); } } else { BlockHighlighting layer = EditorFindSupport.getInstance() .findLayer(c, Factory.INC_SEARCH_LAYER); if (layer != null) { int[] block = layer.gethighlightedBlock(); if (block != null) { c.select(block[0], block[1]); } } // EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); // DrawLayerFactory.IncSearchLayer incLayer // = (DrawLayerFactory.IncSearchLayer)editorUI.findLayer( // DrawLayerFactory.INC_SEARCH_LAYER_NAME); // if (incLayer != null) { // if (incLayer.isEnabled()) { // int offs = incLayer.getOffset(); // int len = incLayer.getLength(); // if (len > 0){ // c.select(offs, offs + len); // } // } // } } } EditorFindSupport.getInstance().incSearchReset(); findPanel.resetBlockSearch(); KeyEventBlocker blocker = findPanel.getBlocker(); if (blocker!=null){ blocker.stopBlocking(false); } } public void windowClosing(WindowEvent e) { hideDialog(); } public void windowClosed(WindowEvent e) { synchronized (dialogLock) { if (findDialog != null){ xPos = findDialog.getLocation().x; yPos = findDialog.getLocation().y; } } Map findProps = findPanel.getFindProps(); EditorFindSupport.getInstance().incSearchReset(); findPanel.resetBlockSearch(); EditorFindSupport.getInstance().setBlockSearchHighlight(0, 0); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.FALSE); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, new Integer(0)); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_END, null); EditorFindSupport.getInstance().putFindProperties(findProps); KeyEventBlocker blocker = findPanel.getBlocker(); if (blocker!=null){ blocker.stopBlocking(false); } ComponentUtils.returnFocus(); } public void showFindDialog(KeyEventBlocker blocker) { showFindDialogImpl(false, blocker); } public void showReplaceDialog(KeyEventBlocker blocker) { showFindDialogImpl(true, blocker); } public void hideDialog() { synchronized (dialogLock) { if (findDialog != null){ xPos = findDialog.getLocation().x; yPos = findDialog.getLocation().y; findDialog.dispose(); } findDialog = null; } } private Vector getHistoryVector(){ List histList = EditorFindSupport.getInstance().getHistory(); if (histList == null) histList = new ArrayList(); boolean isRegExpChecked = ((Boolean)findPanel.getFindProps().get(EditorFindSupport.FIND_REG_EXP)).booleanValue(); Vector vec = new Vector(); for (int i=0; i findPanelMap = findPanel.getFindProps(); SPW spw = new SPW((String)findPanelMap.get(EditorFindSupport.FIND_WHAT), getBooleanProp(EditorFindSupport.FIND_WHOLE_WORDS, findPanelMap), getBooleanProp(EditorFindSupport.FIND_MATCH_CASE, findPanelMap), getBooleanProp(EditorFindSupport.FIND_REG_EXP, findPanelMap)); if (src == findButtons[0]) { // Find button fSup.addToHistory(spw); fSup.putFindProperties(findPanelMap); fSup.find(null, false); updateCaretPosition(); findPerformed = true; } else if (src == findButtons[1]) { // Replace button fSup.addToHistory(spw); findPanel.updateReplaceHistory(); fSup.putFindProperties(findPanelMap); try { if (fSup.replace(null, false)) { // replaced fSup.find(null, false); } } catch (BadLocationException e) { // replace in guarded block if (!ComponentUtils.isGuardedException(e)) { LOG.log(Level.WARNING, e.getMessage(), e); } } updateCaretPosition(); findPerformed = true; } else if (src == findButtons[2]) { // Replace All button fSup.addToHistory(spw); findPanel.updateReplaceHistory(); fSup.putFindProperties(findPanelMap); fSup.replaceAll(null); findPerformed = true; } else if (src == findButtons[3]) { // Cancel button hideDialog(); } } private int getBlockEndOffset(){ Position pos = (Position) EditorFindSupport.getInstance().getFindProperties().get(EditorFindSupport.FIND_BLOCK_SEARCH_END); return (pos != null) ? pos.getOffset() : -1; } /** Panel that holds the find logic */ private class FindPanel extends EditorFindDialogPanel implements ItemListener, KeyListener, ActionListener, FocusListener { private Map findProps = Collections.synchronizedMap(new HashMap(20)); private Map objToProps = Collections.synchronizedMap(new HashMap(20)); private javax.swing.DefaultComboBoxModel findHistory = new javax.swing.DefaultComboBoxModel(); private javax.swing.DefaultComboBoxModel replaceHistory = new javax.swing.DefaultComboBoxModel(); private KeyEventBlocker blocker; private int blockSearchStartPos = 0; private int blockSearchEndPos = 0; FindPanel() { objToProps.put(findWhat, EditorFindSupport.FIND_WHAT); objToProps.put(replaceWith, EditorFindSupport.FIND_REPLACE_WITH); objToProps.put(highlightSearch, EditorFindSupport.FIND_HIGHLIGHT_SEARCH); objToProps.put(incSearch, EditorFindSupport.FIND_INC_SEARCH); objToProps.put(matchCase, EditorFindSupport.FIND_MATCH_CASE); //objToProps.put(smartCase, FindSupport.FIND_SMART_CASE); objToProps.put(wholeWords, EditorFindSupport.FIND_WHOLE_WORDS); objToProps.put(regExp, EditorFindSupport.FIND_REG_EXP); objToProps.put(bwdSearch, EditorFindSupport.FIND_BACKWARD_SEARCH); objToProps.put(wrapSearch, EditorFindSupport.FIND_WRAP_SEARCH); objToProps.put(blockSearch, EditorFindSupport.FIND_BLOCK_SEARCH); findProps.putAll(EditorFindSupport.getInstance().getFindProperties()); revertMap(); findWhat.setModel(findHistory); findWhat.getEditor().setItem(getProperty(findWhat)); replaceWith.setModel(replaceHistory); replaceWith.getEditor().setItem(getProperty(replaceWith)); highlightSearch.setSelected(getBooleanProperty(highlightSearch)); incSearch.setSelected(getBooleanProperty(incSearch)); matchCase.setSelected(getBooleanProperty(matchCase)); //smartCase.setSelected(getBooleanProperty(smartCase)); wholeWords.setSelected(getBooleanProperty(wholeWords)); regExp.setSelected(getBooleanProperty(regExp)); bwdSearch.setSelected(getBooleanProperty(bwdSearch)); wrapSearch.setSelected(getBooleanProperty(wrapSearch)); findWhat.getEditor().getEditorComponent().addKeyListener(this); findWhat.addActionListener(this); replaceWith.getEditor().getEditorComponent().addKeyListener(this); replaceWith.addActionListener(this); highlightSearch.addItemListener(this); incSearch.addItemListener(this); matchCase.addItemListener(this); //smartCase.addItemListener(this); wholeWords.addItemListener(this); regExp.addItemListener(this); bwdSearch.addItemListener(this); wrapSearch.addItemListener(this); blockSearch.addItemListener(this); historyChangeListener = new PropertyChangeListener(){ public void propertyChange(PropertyChangeEvent evt){ if (evt == null || !EditorFindSupport.FIND_HISTORY_CHANGED_PROP.equals(evt.getPropertyName())){ return; } updateFindHistory(); } }; EditorFindSupport.getInstance().addPropertyChangeListener(historyChangeListener); } protected Map getFindProps() { return findProps; } private KeyEventBlocker getBlocker(){ return blocker; } private void putProperty(Object component, Object value) { String prop = (String)objToProps.get(component); if (prop != null) { findProps.put(prop, value); } } private Object getProperty(Object component) { String prop = (String)objToProps.get(component); return (prop != null) ? findProps.get(prop) : null; } private boolean getBooleanProperty(Object component) { Object prop = getProperty(component); return (prop != null) ? ((Boolean)prop).booleanValue() : false; } protected void changeVisibility(boolean v) { replaceWith.setVisible(v); replaceWithLabel.setVisible(v); } public void resetBlockSearch(){ blockSearch.setSelected(false); blockSearch.setEnabled(false); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.FALSE); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, new Integer(0)); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_END, null); blockSearchStartPos = 0; blockSearchEndPos = 0; EditorFindSupport.getInstance().setBlockSearchHighlight(0,0); EditorFindSupport.getInstance().putFindProperties(findProps); } private void initBlockSearch(){ JTextComponent c = DocumentsRegistry.getMostActiveComponent(); String selText = null; int startSelection = 0; int endSelection = 0; boolean blockSearchVisible = false; if (c != null) { startSelection = c.getSelectionStart(); endSelection = c.getSelectionEnd(); Document doc = c.getDocument(); try{ int startLine = DocUtils.getLineOffset(doc, startSelection); int endLine = DocUtils.getLineOffset(doc, endSelection); if (endLine > startLine) { blockSearchVisible = true; } } catch (BadLocationException ble){ } caretPosition = bwdSearch.isSelected() ? c.getSelectionEnd() : c.getSelectionStart(); if (blockSearchVisible == false && dialogInvokedViaKeystroke){ dialogInvokedViaKeystroke = false; selText = c.getSelectedText(); if (selText != null) { int n = selText.indexOf( '\n' ); if (n >= 0 ) selText = selText.substring(0, n); findWhat.getEditor().setItem(selText); changeFindWhat(true); } } blockSearchStartPos = blockSearchVisible ? startSelection : 0; blockSearchEndPos = blockSearchVisible ? endSelection : 0; try{ blockSearch.setEnabled(blockSearchVisible); blockSearch.setSelected(blockSearchVisible); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.valueOf(blockSearchVisible)); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, new Integer(blockSearchStartPos)); int be = getBlockEndOffset(); if (be < 0){ findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_END, doc.createPosition(blockSearchEndPos)); }else{ blockSearchEndPos = be; } EditorFindSupport.getInstance().setBlockSearchHighlight(blockSearchStartPos, blockSearchEndPos); }catch(BadLocationException ble){ blockSearch.setSelected(false); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.FALSE); findProps.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, null); } } } protected void init(boolean isReplace, KeyEventBlocker blocker) { this.blocker = blocker; findHistory.setSelectedItem(null); replaceHistory.setSelectedItem(null); findWhat.getEditor().getEditorComponent().addFocusListener(this); if (isReplace) { replaceWith.getEditor().getEditorComponent().addFocusListener(this); } findProps.putAll(EditorFindSupport.getInstance().getFindProperties()); revertMap(); highlightSearch.setSelected(getBooleanProperty(highlightSearch)); incSearch.setSelected(getBooleanProperty(incSearch)); matchCase.setSelected(getBooleanProperty(matchCase)); //smartCase.setSelected(getBooleanProperty(smartCase)); wholeWords.setSelected(getBooleanProperty(wholeWords)); boolean regExpValue = getBooleanProperty(regExp); regExp.setSelected(regExpValue); wholeWords.setEnabled(!regExpValue); incSearch.setEnabled(!regExpValue); bwdSearch.setSelected(getBooleanProperty(bwdSearch)); wrapSearch.setSelected(getBooleanProperty(wrapSearch)); findHistory = new DefaultComboBoxModel(getHistoryVector()); findWhat.setModel(findHistory); } protected void showNotify() { // fix of issue #66217 boolean focused = findWhat.getEditor().getEditorComponent().requestFocusInWindow(); if (focused == false){ SwingUtilities.invokeLater(new Runnable(){ public void run(){ findWhat.getEditor().getEditorComponent().requestFocusInWindow(); } }); } } private void updateHistory(JComboBox c, javax.swing.DefaultComboBoxModel history) { Object item = c.getEditor().getItem(); if( item != null && !item.equals("")) { //NOI18N history.removeElement(item); history.insertElementAt(item, 0); history.setSelectedItem(null); } c.getEditor().setItem(item); } protected void updateFindHistory() { //updateHistory(findWhat, findHistory); /* List list = new ArrayList(); for (int i = 0; i findProps; private WeakHashMap>> comp2layer = new WeakHashMap>>(); /** Support for firing change events */ private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); private SPW lastSelected; private List historyList; private EditorFindSupport() { // prevent instance creation } /** Get shared instance of find support */ public static EditorFindSupport getInstance() { if (findSupport == null) { findSupport = new EditorFindSupport(); } return findSupport; } public Map getDefaultFindProperties() { HashMap props = new HashMap(); props.put(FIND_WHAT, null); props.put(FIND_REPLACE_WITH, null); props.put(FIND_HIGHLIGHT_SEARCH, Boolean.TRUE); props.put(FIND_INC_SEARCH, Boolean.TRUE); props.put(FIND_BACKWARD_SEARCH, Boolean.FALSE); props.put(FIND_WRAP_SEARCH, Boolean.TRUE); props.put(FIND_MATCH_CASE, Boolean.FALSE); props.put(FIND_SMART_CASE, Boolean.FALSE); props.put(FIND_WHOLE_WORDS, Boolean.FALSE); props.put(FIND_REG_EXP, Boolean.FALSE); props.put(FIND_HISTORY, new Integer(30)); return props; } private int getBlockEndOffset(){ Position pos = (Position) getFindProperties().get(FIND_BLOCK_SEARCH_END); return (pos != null) ? pos.getOffset() : -1; } public Map getFindProperties() { if (findProps == null) { findProps = getDefaultFindProperties(); } return findProps; } /** Get find property with specified name */ public Object getFindProperty(String name) { return getFindProperties().get(name); } private Map getValidFindProperties(Map props) { return (props != null) ? props : getFindProperties(); } /** *

IMPORTANT: This method is public only for keeping backwards * compatibility of the {@link org.netbeans.editor.FindSupport} class. */ public int[] getBlocks(int[] blocks, Document doc, int startPos, int endPos) throws BadLocationException { Map props = getValidFindProperties(null); Boolean b = (Boolean)props.get(FIND_BLOCK_SEARCH); boolean blockSearch = (b != null && b.booleanValue()); Integer i = (Integer) props.get(FIND_BLOCK_SEARCH_START); int blockSearchStart = (i != null) ? i.intValue() : -1; int blockSearchEnd = getBlockEndOffset(); if (blockSearch && blockSearchStart>-1 && blockSearchEnd >0){ if (endPos>=blockSearchStart && startPos <=blockSearchEnd){ startPos = Math.max(blockSearchStart, startPos); endPos = Math.min(blockSearchEnd, endPos); }else{ return blocks; } } return DocumentFinder.findBlocks(doc, startPos, endPos, props, blocks); } /** * Get find property without performing initialization * of find properties. This is useful for example for base document * when it wants to query whether it should do highlight search. * *

IMPORTANT: This method is public only for keeping backwards * compatibility of the {@link org.netbeans.editor.FindSupport} class. */ public Object getPropertyNoInit(String name) { if (findProps == null) { return null; } else { return getFindProperty(name); } } /** Set find property with specified name and fire change. */ public void putFindProperty(String name, Object newValue) { Object oldValue = getFindProperty(name); if ((oldValue == null && newValue == null) || (oldValue != null && oldValue.equals(newValue)) ) { return; } if (newValue != null) { getFindProperties().put(name, newValue); } else { getFindProperties().remove(name); } firePropertyChange(name, oldValue, newValue); } /** Add/replace properties from some other map * to current find properties. If the added properties * are different than the original ones, * the property change is fired. */ public void putFindProperties(Map propsToAdd) { if (!getFindProperties().equals(propsToAdd)) { getFindProperties().putAll(propsToAdd); firePropertyChange(null, null, null); } } public void setBlockSearchHighlight(int startSelection, int endSelection){ JTextComponent comp = DocumentsRegistry.getMostActiveComponent(); BlockHighlighting layer = comp == null ? null : findLayer(comp, Factory.BLOCK_SEARCH_LAYER); if (layer != null) { if (startSelection >= 0 && endSelection >= 0 && startSelection < endSelection ) { layer.highlightBlock( startSelection, endSelection, FontColorNames.BLOCK_SEARCH_COLORING); } else { layer.highlightBlock(-1, -1, FontColorNames.BLOCK_SEARCH_COLORING); } } // TODO: remove // JTextComponent c = DocumentsRegistry.getMostActiveComponent(); // if (c==null) return; // EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); // DrawLayerFactory.BlockSearchLayer blockLayer // = (DrawLayerFactory.BlockSearchLayer)editorUI.findLayer( // DrawLayerFactory.BLOCK_SEARCH_LAYER_NAME); // Boolean b = (Boolean)getFindProperties().get(FIND_BACKWARD_SEARCH); // boolean back = (b != null && b.booleanValue()); // // if (startSelection >= endSelection){ // if (blockLayer != null) { // if (blockLayer.isEnabled()) { // blockLayer.setEnabled(false); // try { // editorUI.repaintBlock(blockLayer.getOffset(), blockLayer.getOffset()+blockLayer.getLength()); // } catch (BadLocationException e) { // LOG.log(Level.WARNING, e.getMessage(), e); // } // } // } // }else{ // //init layer // if (blockLayer == null) { // blockLayer = new DrawLayerFactory.BlockSearchLayer(); // if (!editorUI.addLayer(blockLayer, // DrawLayerFactory.BLOCK_SEARCH_LAYER_VISIBILITY) // ) { // return; // couldn't add layer // } // } else { // if (blockLayer.isEnabled()) { // blockLayer.setEnabled(false); // try { // editorUI.repaintOffset(blockLayer.getOffset()); // } catch (BadLocationException e) { // LOG.log(Level.WARNING, e.getMessage(), e); // } // } // } // // blockLayer.setEnabled(true); // blockLayer.setArea(startSelection, endSelection-startSelection); // try { // editorUI.repaintBlock(startSelection, endSelection); // } catch (BadLocationException e) { // LOG.log(Level.WARNING, e.getMessage(), e); // return; // } // c.getCaret().setDot(back ? endSelection : startSelection); // } } public boolean incSearch(Map props, int caretPos) { props = getValidFindProperties(props); // if regexp terminate incSearch Boolean b = (Boolean)props.get(FIND_REG_EXP); if (b !=null && b.booleanValue()){ return false; } b = (Boolean)props.get(FIND_INC_SEARCH); if (b != null && b.booleanValue()) { // inc search enabled JTextComponent comp = DocumentsRegistry.getMostActiveComponent(); if (comp != null) { b = (Boolean)props.get(FIND_BACKWARD_SEARCH); boolean back = (b != null && b.booleanValue()); b = (Boolean)props.get(FIND_BLOCK_SEARCH); boolean blockSearch = (b != null && b.booleanValue()); Integer i = (Integer) props.get(FIND_BLOCK_SEARCH_START); int blockSearchStart = (i != null) ? i.intValue() : -1; Position endPos = (Position) props.get(FIND_BLOCK_SEARCH_END); int blockSearchEnd = (endPos != null) ? endPos.getOffset() : -1; int endOffset = (back) ? 0 : -1; int pos; try { int start = (blockSearch && blockSearchStart > -1) ? blockSearchStart : 0; int end = (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : -1; if (start>0 && end == -1) return false; int findRet[] = findInBlock(comp, caretPos, start, end, props, false); if (findRet == null) { incSearchReset(); return false; } pos = findRet[0]; } catch (BadLocationException e) { LOG.log(Level.WARNING, e.getMessage(), e); return false; } // Find the layer BlockHighlighting layer = (BlockHighlighting)findLayer(comp, Factory.INC_SEARCH_LAYER); if (pos >= 0) { String s = (String)props.get(FIND_WHAT); int len = (s != null) ? s.length() : 0; if (len > 0) { if (comp.getSelectionEnd() > comp.getSelectionStart()){ comp.select(caretPos, caretPos); } // TODO: remove // incLayer.setInversion(!blockSearch); // incLayer.setEnabled(true); // incLayer.setArea(pos, len); if (layer != null) { layer.highlightBlock( pos, pos + len, blockSearch ? FontColorNames.INC_SEARCH_COLORING : FontColorNames.SELECTION_COLORING ); } // reset higlighting Map defaultProps = getValidFindProperties(null); String findWhatDef = (String)defaultProps.get(FIND_WHAT); if (findWhatDef!=null && findWhatDef.length()>0){ defaultProps.put(FIND_WHAT, ""); //NOI18N comp.repaint(); } ensureVisible(comp, pos, pos); return true; } } else { // string not found // !!! ((BaseCaret)c.getCaret()).dispatchUpdate(); } } } else { // inc search not enabled incSearchReset(); } return false; } public void incSearchReset() { // Find the layer JTextComponent comp = DocumentsRegistry.getMostActiveComponent(); BlockHighlighting layer = comp == null ? null : (BlockHighlighting)findLayer(comp, Factory.INC_SEARCH_LAYER); if (layer != null) { layer.highlightBlock(-1, -1, null); } } private boolean isBackSearch(Map props, boolean oppositeDir) { Boolean b = (Boolean)props.get(FIND_BACKWARD_SEARCH); boolean back = (b != null && b.booleanValue()); if (oppositeDir) { back = !back; } return back; } private void selectText(JTextComponent c, int start, int end, boolean back){ Caret caret = c.getCaret(); ensureVisible(c, start, end); if (back) { caret.setDot(end); caret.moveDot(start); } else { // forward direction caret.setDot(start); caret.moveDot(end); } } private void ensureVisible(JTextComponent c, int startOffset, int endOffset) { // TODO: read insets from settings ensureVisible(c, startOffset, endOffset, new Insets(10, 10, 10, 10)); } /** * Ensure that the given region will be visible in the view * with the appropriate find insets. */ private void ensureVisible(JTextComponent c, int startOffset, int endOffset, Insets extraInsets) { try { Rectangle startBounds = c.modelToView(startOffset); Rectangle endBounds = c.modelToView(endOffset); if (startBounds != null && endBounds != null) { startBounds.add(endBounds); if (extraInsets != null) { Rectangle visibleBounds = c.getVisibleRect(); int extraTop = (extraInsets.top < 0) ? -extraInsets.top * visibleBounds.height / 100 // percentage : extraInsets.top * endBounds.height; // line count startBounds.y -= extraTop; startBounds.height += extraTop; startBounds.height += (extraInsets.bottom < 0) ? -extraInsets.bottom * visibleBounds.height / 100 // percentage : extraInsets.bottom * endBounds.height; // line count int extraLeft = (extraInsets.left < 0) ? -extraInsets.left * visibleBounds.width / 100 // percentage : extraInsets.left * endBounds.width; // char count startBounds.x -= extraLeft; startBounds.width += extraLeft; startBounds.width += (extraInsets.right < 0) ? -extraInsets.right * visibleBounds.width / 100 // percentage : extraInsets.right * endBounds.width; // char count } c.scrollRectToVisible(startBounds); } } catch (BadLocationException e) { // do not scroll } } private FindReplaceResult findReplaceImpl(String replaceExp, Map props, boolean oppositeDir){ incSearchReset(); props = getValidFindProperties(props); boolean back = isBackSearch(props, oppositeDir); JTextComponent c = DocumentsRegistry.getMostActiveComponent(); Object findWhat = props.get(FIND_WHAT); if (findWhat == null) { // nothing to search for return null; } String exp = "'" + findWhat + "' "; // NOI18N if (c != null) { ComponentUtils.clearStatusText(c); Caret caret = c.getCaret(); int dotPos = caret.getDot(); if (findWhat.equals(c.getSelectedText())) { Object dp = props.get(FIND_BACKWARD_SEARCH); boolean direction = (dp != null) ? ((Boolean)dp).booleanValue() : false; if (dotPos == (oppositeDir ^ direction ? c.getSelectionEnd() : c.getSelectionStart())) dotPos += (oppositeDir ^ direction ? -1 : 1); } Boolean b = (Boolean)props.get(FIND_BLOCK_SEARCH); boolean blockSearch = (b != null && b.booleanValue()); Integer i = (Integer) props.get(FIND_BLOCK_SEARCH_START); int blockSearchStart = (i != null) ? i.intValue() : -1; int blockSearchEnd = getBlockEndOffset(); try { FindReplaceResult result = findReplaceInBlock(replaceExp, c, dotPos, (blockSearch && blockSearchStart > -1) ? blockSearchStart : 0, (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : -1, props, oppositeDir); int[] blk = null; if (result != null){ blk = result.getFoundPositions(); } if (blk != null) { selectText(c, blk[0], blk[1], back); DocumentsJumpList.checkAddEntry(); String msg = exp + NbBundle.getBundle(EditorFindSupport.class).getString(FOUND_LOCALE) + ' ' + DocUtils.debugPosition(c.getDocument(), blk[0]); if (blk[2] == 1) { // wrap was done msg += "; "; // NOI18N if (blockSearch && blockSearchEnd>0 && blockSearchStart >-1){ msg += back ? NbBundle.getBundle(EditorFindSupport.class).getString(WRAP_BLOCK_END_LOCALE) : NbBundle.getBundle(EditorFindSupport.class).getString(WRAP_BLOCK_START_LOCALE); }else{ msg += back ? NbBundle.getBundle(EditorFindSupport.class).getString(WRAP_END_LOCALE) : NbBundle.getBundle(EditorFindSupport.class).getString(WRAP_START_LOCALE); } ComponentUtils.setStatusBoldText(c, msg); c.getToolkit().beep(); } else { ComponentUtils.setStatusText(c, msg); } return result; } else { // not found ComponentUtils.setStatusBoldText(c, exp + NbBundle.getBundle(EditorFindSupport.class).getString( NOT_FOUND_LOCALE)); // issue 14189 - selection was not removed c.getCaret().setDot(c.getCaret().getDot()); } } catch (BadLocationException e) { LOG.log(Level.WARNING, e.getMessage(), e); } } return null; } /** Find the text from the caret position. * @param props search properties * @param oppositeDir whether search in opposite direction */ public boolean find(Map props, boolean oppositeDir) { FindReplaceResult result = findReplaceImpl(null, props, oppositeDir); return (result != null); } private FindReplaceResult findReplaceInBlock(String replaceExp, JTextComponent c, int startPos, int blockStartPos, int blockEndPos, Map props, boolean oppositeDir) throws BadLocationException { if (c != null) { props = getValidFindProperties(props); Document doc = (Document)c.getDocument(); int pos = -1; boolean wrapDone = false; String replaced = null; boolean back = isBackSearch(props, oppositeDir); Boolean b = (Boolean)props.get(FIND_WRAP_SEARCH); boolean wrap = (b != null && b.booleanValue()); int docLen = doc.getLength(); if (blockEndPos == -1) { blockEndPos = docLen; } int retFind[]; while (true) { //pos = doc.find(sf, startPos, back ? blockStartPos : blockEndPos); int off1 = startPos; int off2 = back ? blockStartPos : blockEndPos; FindReplaceResult result = DocumentFinder.findReplaceResult(replaceExp, doc, Math.min(off1, off2), Math.max(off1, off2), props, oppositeDir ); if (result == null){ return null; } retFind = result.getFoundPositions(); replaced = result.getReplacedString(); if (retFind == null){ break; } pos = retFind[0]; if (pos != -1) { break; } if (wrap) { if (back) { //Bug #20552 the wrap search check whole document //instead of just the remaining not-searched part to be //able to find expressions with the cursor in it //blockStartPos = startPos; startPos = blockEndPos; } else { //blockEndPos = startPos; startPos = blockStartPos; } wrapDone = true; wrap = false; // only one loop } else { // no wrap set break; } } if (pos != -1) { int[] ret = new int[3]; ret[0] = pos; ret[1] = retFind[1]; ret[2] = wrapDone ? 1 : 0; return new FindReplaceResult(ret, replaced); } } return null; } /** Find the searched expression * @param startPos position from which to search. It must be inside the block. * @param blockStartPos starting position of the block. It must * be valid position greater or equal than zero. It must be lower than * or equal to blockEndPos (except blockEndPos=-1). * @param blockEndPos ending position of the block. It can be -1 for the end * of document. It must be greater or equal than blockStartPos (except blockEndPos=-1). * @param props search properties * @param oppositeDir whether search in opposite direction * @param displayWrap whether display messages about the wrapping * @return either null when nothing was found or integer array with three members * ret[0] - starting position of the found string * ret[1] - ending position of the found string * ret[2] - 1 or 0 when wrap was or wasn't performed in order to find the string */ public int[] findInBlock(JTextComponent c, int startPos, int blockStartPos, int blockEndPos, Map props, boolean oppositeDir) throws BadLocationException { FindReplaceResult result = findReplaceInBlock(null, c, startPos, blockStartPos, blockEndPos, props, oppositeDir); return result == null ? null : result.getFoundPositions(); } public boolean replace(Map props, boolean oppositeDir) throws BadLocationException { incSearchReset(); props = getValidFindProperties(props); Boolean b = (Boolean)props.get(FIND_BACKWARD_SEARCH); boolean back = (b != null && b.booleanValue()); if (oppositeDir) { back = !back; } b = (Boolean)props.get(FIND_BLOCK_SEARCH); boolean blockSearch = (b != null && b.booleanValue()); Integer i = (Integer) props.get(FIND_BLOCK_SEARCH_START); int blockSearchStart = (i != null) ? i.intValue() : -1; int blockSearchEnd = getBlockEndOffset(); JTextComponent c = DocumentsRegistry.getMostActiveComponent(); if (c != null) { String s = (String)props.get(FIND_REPLACE_WITH); Caret caret = c.getCaret(); if (caret.isSelectionVisible()){ int dotPos = caret.getDot(); Object dp = props.get(FIND_BACKWARD_SEARCH); boolean direction = (dp != null) ? ((Boolean)dp).booleanValue() : false; dotPos = (oppositeDir ^ direction ? c.getSelectionEnd() : c.getSelectionStart()); c.setCaretPosition(dotPos); } FindReplaceResult result = findReplaceImpl(s, props, oppositeDir); if (result!=null){ s = result.getReplacedString(); } else { return false; } Document doc = (Document)c.getDocument(); int startPos = c.getSelectionStart(); int len = c.getSelectionEnd() - startPos; DocUtils.atomicLock(doc); try { if (len > 0) { doc.remove(startPos, len); } if (s != null && s.length() > 0) { doc.insertString(startPos, s, null); } } finally { DocUtils.atomicUnlock(doc); if (blockSearch){ setBlockSearchHighlight(blockSearchStart, getBlockEndOffset()); } } // adjust caret pos after replace operation int adjustedCaretPos = (back || s == null) ? startPos : startPos + s.length(); caret.setDot(adjustedCaretPos); } return true; } public void replaceAll(Map props) { incSearchReset(); JTextComponent c = DocumentsRegistry.getMostActiveComponent(); Document doc = (Document)c.getDocument(); int maxCnt = doc.getLength(); int replacedCnt = 0; int totalCnt = 0; props = getValidFindProperties(props); props = new HashMap(props); String replaceWithOriginal = (String)props.get(FIND_REPLACE_WITH); Boolean b = (Boolean)props.get(FIND_BLOCK_SEARCH); boolean blockSearch = (b != null && b.booleanValue()); b = (Boolean)props.get(FIND_WRAP_SEARCH); boolean wrapSearch = (b != null && b.booleanValue()); b = (Boolean)props.get(FIND_BACKWARD_SEARCH); boolean backSearch = (b != null && b.booleanValue()); if (wrapSearch){ props.put(FIND_WRAP_SEARCH, Boolean.FALSE); props.put(FIND_BACKWARD_SEARCH, Boolean.FALSE); firePropertyChange(null, null, null); } Integer i = (Integer) props.get(FIND_BLOCK_SEARCH_START); int blockSearchStart = (i != null) ? i.intValue() : -1; int blockSearchEnd = getBlockEndOffset(); if (c != null) { DocUtils.atomicLock(doc); try { int startPosWholeSearch = 0; int endPosWholeSearch = -1; int caretPos = c.getCaret().getDot(); if (!wrapSearch){ if (backSearch){ startPosWholeSearch = 0; endPosWholeSearch = caretPos; }else{ startPosWholeSearch = caretPos; endPosWholeSearch = -1; } } int actualPos = wrapSearch ? 0 : c.getCaret().getDot(); int pos = (blockSearch && blockSearchStart > -1) ? ( backSearch ? blockSearchEnd : blockSearchStart) : actualPos; // actual position while (true) { blockSearchEnd = getBlockEndOffset(); FindReplaceResult result = findReplaceInBlock(replaceWithOriginal, c, pos, (blockSearch && blockSearchStart > -1) ? blockSearchStart : startPosWholeSearch, (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : endPosWholeSearch, props, false); if (result == null){ break; } int[] blk = result.getFoundPositions(); String replaceWith = result.getReplacedString(); if (blk == null) { break; } totalCnt++; int len = blk[1] - blk[0]; boolean skip = false; // cannot remove (because of guarded block)? try { doc.remove(blk[0], len); } catch (BadLocationException e) { // replace in guarded block if (ComponentUtils.isGuardedException(e)) { skip = true; } else { throw e; } } if (skip) { pos = blk[0] + len; } else { // can and will insert the new string if (replaceWith != null && replaceWith.length() > 0) { doc.insertString(blk[0], replaceWith, null); } pos = blk[0] + ((replaceWith != null) ? replaceWith.length() : 0); replacedCnt++; } } // Display message about replacement if (totalCnt == 0){ Object findWhat = props.get(FIND_WHAT); String exp = "'' "; //NOI18N if (findWhat != null) { // nothing to search for exp = "'" + findWhat + "' "; // NOI18N } ComponentUtils.setStatusBoldText(c, exp + NbBundle.getBundle(EditorFindSupport.class).getString( NOT_FOUND_LOCALE)); }else{ MessageFormat fmt = new MessageFormat( NbBundle.getBundle(EditorFindSupport.class).getString(ITEMS_REPLACED_LOCALE)); String msg = fmt.format(new Object[] { new Integer(replacedCnt), new Integer(totalCnt) }); ComponentUtils.setStatusText(c, msg); } } catch (BadLocationException e) { LOG.log(Level.WARNING, e.getMessage(), e); } finally { DocUtils.atomicUnlock(doc); if (blockSearch){ setBlockSearchHighlight(blockSearchStart, getBlockEndOffset()); } } } } public void hookLayer(BlockHighlighting layer, JTextComponent component) { synchronized (comp2layer) { Map> type2layer = comp2layer.get(component); if (type2layer == null) { type2layer = new HashMap>(); comp2layer.put(component, type2layer); } type2layer.put(layer.getLayerTypeId(), new WeakReference(layer)); } } public void unhookLayer(BlockHighlighting layer, JTextComponent component) { synchronized (comp2layer) { Map> type2layer = comp2layer.get(component); if (type2layer != null) { type2layer.remove(layer.getLayerTypeId()); if (type2layer.isEmpty()) { comp2layer.remove(component); } } } } public BlockHighlighting findLayer(JTextComponent component, String layerId) { synchronized (comp2layer) { Map> type2layer = comp2layer.get(component); BlockHighlighting layer = null; if (type2layer != null) { WeakReference ref = type2layer.get(layerId); if (ref != null) { layer = ref.get(); } } return layer; } } // TODO: remove // /** Get position of wrap mark for some document */ // public int getWrapSearchMarkPos(Document doc) { // Mark mark = (Mark)doc.getProperty(Document.WRAP_SEARCH_MARK_PROP); // try { // return (mark != null) ? mark.getOffset() : doc.getLength(); // } catch (InvalidMarkException e) { // throw new RuntimeException(); // shouldn't happen // } // } // // /** Set new position of wrap mark for some document */ // public void setWrapSearchMarkPos(Document doc, int pos) { // //!!! // } /** Add weak listener to listen to change of any property. The caller must * hold the listener object in some instance variable to prevent it * from being garbage collected. */ public void addPropertyChangeListener(PropertyChangeListener l) { changeSupport.addPropertyChangeListener(l); } public synchronized void addPropertyChangeListener(String findPropertyName, PropertyChangeListener l) { changeSupport.addPropertyChangeListener(findPropertyName, l); } /** Remove listener for changes in properties */ public void removePropertyChangeListener(PropertyChangeListener l) { changeSupport.removePropertyChangeListener(l); } /** *

IMPORTANT: This method is public only for keeping backwards * compatibility of the {@link org.netbeans.editor.FindSupport} class. */ public void firePropertyChange(String settingName, Object oldValue, Object newValue) { changeSupport.firePropertyChange(settingName, oldValue, newValue); } public void setHistory(List spwList){ this.historyList = new ArrayList(spwList); firePropertyChange(FIND_HISTORY_CHANGED_PROP,null,null); } public List getHistory(){ return historyList; } public void setLastSelected(SPW spw){ this.lastSelected = spw; Map props = getFindProperties(); if (spw == null) return; props.put(FIND_WHAT, spw.getSearchExpression()); props.put(FIND_MATCH_CASE, Boolean.valueOf(spw.isMatchCase())); props.put(FIND_REG_EXP, Boolean.valueOf(spw.isRegExp())); props.put(FIND_WHOLE_WORDS, Boolean.valueOf(spw.isWholeWords())); } public SPW getLastSelected(){ return lastSelected; } public void addToHistory(SPW spw){ if (spw == null) return; firePropertyChange(FIND_HISTORY_PROP, null, spw); } public final static class SPW{ private String searchExpression; private boolean wholeWords; private boolean matchCase; private boolean regExp; public SPW(String searchExpression, boolean wholeWords, boolean matchCase, boolean regExp){ this.searchExpression = searchExpression; this.wholeWords = wholeWords; this.matchCase = matchCase; this.regExp = regExp; } /** @return searchExpression */ public String getSearchExpression(){ return searchExpression; } /** @return true if the wholeWords parameter was used during search performing */ public boolean isWholeWords(){ return wholeWords; } /** @return true if the matchCase parameter was used during search performing */ public boolean isMatchCase(){ return matchCase; } /** @return true if the regExp parameter was used during search performing */ public boolean isRegExp(){ return regExp; } public boolean equals(Object obj){ if (!(obj instanceof SPW)){ return false; } SPW sp = (SPW)obj; return (this.searchExpression.equals(sp.getSearchExpression()) && this.wholeWords == sp.isWholeWords() && this.matchCase == sp.isMatchCase() && this.regExp == sp.isRegExp()); } public int hashCode() { int result = 17; result = 37*result + (this.wholeWords ? 1:0); result = 37*result + (this.matchCase ? 1:0); result = 37*result + (this.regExp ? 1:0); result = 37*result + this.searchExpression.hashCode(); return result; } public String toString(){ StringBuffer sb = new StringBuffer("[SearchPatternWrapper:]\nsearchExpression:"+searchExpression);//NOI18N sb.append('\n'); sb.append("wholeWords:");//NOI18N sb.append(wholeWords); sb.append('\n'); sb.append("matchCase:");//NOI18N sb.append(matchCase); sb.append('\n'); sb.append("regExp:");//NOI18N sb.append(regExp); return sb.toString(); } } // End of SPW class } Directory: /editor/lib2/src/org/netbeans/spi/editor/ ==================================================== File [added]: DialogFactory.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/DialogFactory.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 58 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor; import java.awt.Dialog; import java.awt.event.*; import javax.swing.*; /** * DialogFactory implementation is a class responsible for providing * proper implementation of Dialog containing required widgets. * It can provide the dialog itself or delegate the functionality * to another piece of code, e.g some windowing system. * * @author pnejedly * @version 1.0 */ public interface DialogFactory { /** * The method for creating a dialog with specified properties. * * @param title The title of created dialog. * @param panel The content of the dialog to be displayed. * @param modal Whether the dialog should be modal. * @param buttons The array of JButtons to be added to the dialog. * @param sidebuttons The buttons could be placed under the panel (false), * or on the right side of the panel (true). * @param defaultIndex The index of default button in the buttons array, * if index < 0, no default button is set. * @param cancelIndex The index of cancel button - the button that will * be pressed when closing the dialog. * @param listener The listener which will be notified of all button * events. * * @return newly created Dialog */ public Dialog createDialog( String title, JPanel panel, boolean modal, JButton[] buttons, boolean sidebuttons, int defaultIndex, int cancelIndex, ActionListener listener ); } File [added]: EditorImplementationProvider.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/EditorImplementationProvider.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 53 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor; import java.util.ResourceBundle; import javax.swing.Action; import javax.swing.text.JTextComponent; /** This is provider of implementation. This package (org.netbeans.editor) * represent editor core which can be used independently on the rest of NetBeans. * However this core needs access to higher level functionality like access * to localized bundles, access to settings storage, etc. which can be implemented * differently by the applications which uses this editor core. For this purpose * was created this abstract class and it can be extended with any other methods which * are more and more often required by core editor. Example implementation * of this provider can be found in org.netbeans.modules.editor package * * @author David Konecny * @since 10/2001 */ public interface EditorImplementationProvider { /** Returns ResourceBundle for the given class.*/ public ResourceBundle getResourceBundle(String localizer); /** This is temporary method which allows core editor to access * glyph gutter action. These actions are then used when user clicks * on glyph gutter. In next version this should be removed and redesigned * as suggested in issue #16762 */ public Action[] getGlyphGutterActions(JTextComponent target); /** Activates the given component or one of its ancestors. * @return whether the component or one of its ancestors was succesfuly activated * */ public boolean activateComponent(JTextComponent c); } Directory: /editor/lib2/src/org/netbeans/spi/editor/highlighting/ ================================================================= File [added]: AttributesUtilities.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/AttributesUtilities.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 388 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import org.openide.util.Utilities; /** * A collection of utility menthods for working with AttributeSets. * * @author Vita Stejskal */ public final class AttributesUtilities { /** * Creates an immutable AttributeSet, which will contain the * keyValuePairs attributes. If the pairs * contain attributes with the same name the resulting AttributeSet * will return value of the first attribute it will find going through * the pairs in the order as they were passed in. * * @param keyValuePairs The contents of the AttributeSet created * by this method. This parameter should list pairs * of key-value objects; each pair defining one attribute. * * @return The new immutable AttributeSet. */ public static AttributeSet createImmutable(Object... keyValuePairs) { assert keyValuePairs.length % 2 == 0 : "There must be even number of prameters. " + "They are key-value pairs of attributes that will be inserted into the set."; HashMap map = new HashMap(); for(int i = keyValuePairs.length / 2 - 1; i >= 0 ; i--) { Object attrKey = keyValuePairs[2 * i]; Object attrValue = keyValuePairs[2 * i + 1]; map.put(attrKey, attrValue); } return new Immutable(map); } /** * Creates an immutable AttributeSet as a copy of AttributeSets * passed into this method. If the AttributeSets * contain attributes with the same name the resulting AttributeSet * will return value of the first attribute it will find going through * the sets in the order as they were passed in. * * @param sets The AttributeSets which attributes will become * a contents of the newly created AttributeSet. * * @return The new immutable AttributeSet. */ public static AttributeSet createImmutable(AttributeSet... sets) { HashMap map = new HashMap(); for(int i = sets.length - 1; i >= 0; i--) { AttributeSet set = sets[i]; for(Enumeration keys = set.getAttributeNames(); keys.hasMoreElements(); ) { Object attrKey = keys.nextElement(); Object attrValue = set.getAttribute(attrKey); map.put(attrKey, attrValue); } } return new Immutable(map); } /** * Creates a proxy AttributeSet that will delegate to the * AttributeSets passed in as a parameter. If the AttributeSets * contain attributes with the same name the composite AttributeSet * will return value of the first attribute it will find going through * the sets in the order as they were passed in. * * @param sets The AttributeSets to delegate to. * * @return The new composite AttributeSet that will delegate * to the sets passed in. */ public static AttributeSet createComposite(AttributeSet... sets) { if (sets.length == 0) { return SimpleAttributeSet.EMPTY; } else if (sets.length == 1) { return sets[0]; } else { ArrayList all = new ArrayList(); for(AttributeSet s : sets) { if (s instanceof AttributesUtilities.Composite) { all.addAll(((AttributesUtilities.Composite) s).getDelegates()); } else if (s instanceof AttributesUtilities.Proxy) { all.add(((AttributesUtilities.Proxy) s).getDelegate()); } else if (s != null && s != SimpleAttributeSet.EMPTY) { all.add(s); } } if (all.size() == 0) { return SimpleAttributeSet.EMPTY; } else { return new Composite(all.toArray(new AttributeSet[all.size()])); } } } /* package */ static List dismantle(AttributeSet set) { ArrayList sets = new ArrayList(); if (set instanceof Proxy) { sets.addAll(dismantle(((Proxy) set).getDelegate())); } else if (set instanceof Composite) { List delegates = ((Composite) set).getDelegates(); for(AttributeSet delegate : delegates) { sets.addAll(dismantle(delegate)); } } else { sets.add(set); } return sets; } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private AttributesUtilities() { // no-op, just to prevent instantiation } private static class Immutable implements AttributeSet { private final HashMap attribs; private AttributeSet parent = null; /** Creates a new instance of SmartAttributeSet */ private Immutable(HashMap attribs) { this.attribs = attribs == null ? new HashMap() : attribs; } public synchronized void setResolveParent(AttributeSet parent) { this.parent = parent; } public synchronized boolean containsAttributes(AttributeSet attributes) { for(Enumeration names = attributes.getAttributeNames(); names.hasMoreElements(); ) { Object name = names.nextElement(); Object value = attributes.getAttribute(name); if (!containsAttribute(name, value)) { return false; } } return true; } public synchronized boolean isEqual(AttributeSet attr) { return containsAttributes(attr) && attr.containsAttributes(this); } public synchronized Object getAttribute(Object key) { // Somebody is asking for the parent if (AttributeSet.ResolveAttribute == key) { return parent; } // Get the normal value if (attribs.containsKey(key)) { return attribs.get(key); } // Value not found, try parent if we have any if (parent != null) { return parent.getAttribute(key); } else { return null; } } public synchronized boolean isDefined(Object key) { return attribs.containsKey(key); } public synchronized boolean containsAttribute(Object key, Object value) { if (attribs.containsKey(key)) { Object attrValue = attribs.get(key); if (Utilities.compareObjects(value, attrValue)) { return true; } } return false; } public AttributeSet copyAttributes() { return new Proxy(this); } /** * This is really slow don't use it! */ public synchronized int getAttributeCount() { return attribs.size(); } /** * This is really slow don't use it! */ public synchronized Enumeration getAttributeNames() { return Collections.enumeration(attribs.keySet()); } public synchronized AttributeSet getResolveParent() { return parent; } } // End of Immutable class private static final class Proxy implements AttributeSet { private AttributeSet original; public Proxy(AttributeSet original) { this.original = original; } public AttributeSet getDelegate() { return original; } public boolean isEqual(AttributeSet attr) { return original.isEqual(attr); } public boolean containsAttributes(AttributeSet attributes) { return original.containsAttributes(attributes); } public boolean isDefined(Object attrName) { return original.isDefined(attrName); } public Object getAttribute(Object key) { return original.getAttribute(key); } public AttributeSet getResolveParent() { return original.getResolveParent(); } public Enumeration getAttributeNames() { return original.getAttributeNames(); } public int getAttributeCount() { return original.getAttributeCount(); } public AttributeSet copyAttributes() { return original.copyAttributes(); } public boolean containsAttribute(Object name, Object value) { return original.containsAttribute(name, value); } } // End of Proxy class private static final class Composite implements AttributeSet { private final AttributeSet [] delegates; public Composite(AttributeSet... delegates) { this.delegates = delegates; } public List getDelegates() { return Arrays.asList(delegates); } public boolean isEqual(AttributeSet attr) { return containsAttributes(attr) && attr.containsAttributes(this); } public boolean containsAttributes(AttributeSet attributes) { for(Enumeration keys = attributes.getAttributeNames(); keys.hasMoreElements(); ) { Object key = keys.nextElement(); Object value = attributes.getAttribute(key); if (!containsAttribute(key, value)) { return false; } } return true; } public boolean isDefined(Object key) { for(AttributeSet delegate : delegates) { if (delegate.isDefined(key)) { return true; } } return false; } public Object getAttribute(Object key) { for(AttributeSet delegate : delegates) { if (delegate.isDefined(key)) { return delegate.getAttribute(key); } } return null; } public AttributeSet getResolveParent() { return null; } public Enumeration getAttributeNames() { return Collections.enumeration(getAllKeys()); } public int getAttributeCount() { return getAllKeys().size(); } public AttributeSet copyAttributes() { return createImmutable(delegates); } public boolean containsAttribute(Object key, Object value) { for(AttributeSet delegate : delegates) { if (delegate.containsAttribute(key, value)) { return true; } } return false; } private Collection getAllKeys() { HashSet allKeys = new HashSet(); for(AttributeSet delegate : delegates) { for(Enumeration keys = delegate.getAttributeNames(); keys.hasMoreElements(); ) { Object key = keys.nextElement(); allKeys.add(key); } } return allKeys; } } // End of Composite class } File [added]: HighlightsChangeEvent.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsChangeEvent.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 77 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.EventObject; /** * An event object notifying about a change in highlighting of certain area * of a document. The area where the highlighting has changed is specified by * its starting and ending offsets. Whoever receives this event should consider * re-requesting the new list of highlighted areas from the * HighlightsContainer that fired the event. * * @author Vita Stejskal */ public final class HighlightsChangeEvent extends EventObject { private int startOffset; private int endOffset; /** * Creates a new instance of HighlightsChangeEvent. The * startOffset and endOffset parameters specify * the area of a document where highlighting has changed. It's possible to * use Integer.MAX_VALUE for the endOffset parameter * meaning that the end of the change is unknown or the change spans up to * the end of a document. * * @param source The highlight layer that fired this event. * @param startOffset The beginning of the area that has changed. * @param endOffset The end of the changed area. */ public HighlightsChangeEvent(HighlightsContainer source, int startOffset, int endOffset) { super(source); this.startOffset = startOffset; this.endOffset = endOffset; } /** * Gets the beginning of an area in the document where highlighting has * changed. * * @return The starting offset of the chaged area. Should always be greater than * or equal to zero. */ public int getStartOffset() { return startOffset; } /** * Gets the end of an area in the document where highlighting has * changed. * * @return The ending offset of the chaged area. May return Integer.MAX_VALUE * if the ending position is unknown. */ public int getEndOffset() { return endOffset; } } File [added]: HighlightsChangeListener.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsChangeListener.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 38 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.EventListener; /** * The listener of changes in a HighlightsContainer. * * @author Vita Stejskal */ public interface HighlightsChangeListener extends EventListener { /** * The changes notification callback method. * * @param event The event object describing the change. */ public void highlightChanged(HighlightsChangeEvent event); } File [added]: HighlightsContainer.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsContainer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 102 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import javax.swing.text.Position; /** * The container of highlighted areas and their attributes. * * @author vita */ public interface HighlightsContainer { /** * The attribute key for highlights that need to span across a whole line. * *

Typically highlights only affect rendering of a small part of text * (perhaps just several characters). Some layers, however, need to highlight * a whole line in an editor window regardless of how much text the line * contains. The highlighting of a line with a caret is an example of such a layer. * *

If you want a highlight that spans accross the whole editor pane you * can add this attribute key to the highlight's AttributeSet * and set its value to Boolean.TRUE. The highlighted area must * contain the new-line character at the end of the line. */ static final String ATTR_EXTENDS_EOL = new String("org.netbeans.spi.editor.highlighting.HighlightsContainer.ATTR_EXTENDS_EOL"); //NOI18N /** * The attribute key for highlights that need to show up on empty lines. * *

If you use this key for a highlight which contains the new-line character * at the end of an empty line and set the value of this attribute to * Boolean.TRUE then the highlight will be drawn as * a half-character-wide stripe at the beginning of the line. */ static final String ATTR_EXTENDS_EMPTY_LINE = new String("org.netbeans.spi.editor.highlighting.HighlightsContainer.ATTR_EXTENDS_EMPTY_LINE"); //NOI18N /** * Provides the list of highlighted areas that should be used for rendering * a document. * *

The returned highlighted areas (highlights) must obey the following rules: *

    *
  • The starting and ending offsets of each highlight should be * within the range specified by the startOffset and endOffset * parameters. Any highlights outside of this range will be clipped by the * rendering infrastructure. *
  • The highlights must not overlap. The infrastructure may ignore or trim * any overlapping highlights. *
  • The list of highlights must be sorted by their * starting offsets ascendingly (i.e. the smallest offset first). *
* *

The editor infrastructure will log any problems it may encounter with * provided implementations of this interface. Although the infrastructure * will try to do its best to render all highlights supplied by the implementors, * if the above rules are violated the results can't be garanteed. * * @param startOffset The starting offset of the area which the caller * attempts to repaint (or create views for). The staring offset is always >=0. * @param endOffset The ending offset of the rendered area. The Integer.MAX_VALUE * can be passed in if the end offset is unknown to the caller. * The highlights container is then expected to return all highlights * up to the end of the document. * * @return non-null iterator of highlights sorted by offsets. */ HighlightsSequence getHighlights(int startOffset, int endOffset); /** * Adds a listener to this highlights container. * * @param listener The listener to add. */ void addHighlightsChangeListener(HighlightsChangeListener listener); /** * Removes a listener from this highlights container. * * @param listener The listener to remove. */ void removeHighlightsChangeListener(HighlightsChangeListener listener); } File [added]: HighlightsLayer.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsLayer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 216 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.Collection; import java.util.List; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.modules.editor.lib2.highlighting.HighlightingSpiPackageAccessor; import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerAccessor; import org.openide.util.TopologicalSortException; /** * The highlight layer defines a set of highlights used for rendering a document. * *

There can be multiple highlight layers participating in rendering one * document. Each highlight layer provides its z-order, which defines an order * in which the layers are used. The higher the z-order the more 'visible' the * layer is. In other word highlights provided by a layer with higher * z-order can hide highlights provided by a layer with lower z-order * for the same part of a document. * *

The highlights provided by a HighlightsLayer can * define any attributes affecting text rendering including attributes that affect the * size of the view containing the rendered text. The layers that use those attributes * have to participate in the view layout and therefore need to be treated in a * special way. The isFixedSize attribute can be true * if and only if layer's highlights will never contain attributes that influence the * layout of a view (e.g. font size). * *

*

For example attributes changing foreground or background color such as * {@link javax.swing.text.StyleConstants#Foreground} * or {@link javax.swing.text.StyleConstants#Background} * do not affect metrics while the following attributes do affect it * {@link javax.swing.text.StyleConstants#FontFamily}, * {@link javax.swing.text.StyleConstants#FontSize}, * {@link javax.swing.text.StyleConstants#Bold}, * {@link javax.swing.text.StyleConstants#Italic}. *

* * @author Miloslav Metelka * @author Vita Stejskal * @version 1.00 */ public final class HighlightsLayer { /** * Creates a new HighlightsLayer with contents defined by * HighlightsContainer. * * @param layerTypeId The unique identifier of the new layer. This id is * used for identifying this layer among other layers that may be created * for the same Document and it is used for example for * the purpose of z-order. * @param zOrder The layer's z-order. * @param fixedSize Whether this layer defines any attributes * affecting text rendering; including attributes that affect the size * of the view containing the rendered text. Pass in true * if and only if the layer does not change metrics of rendered * text. * @param container The HighlightsContainer that should be used * as a contents of this layer. * * @see org.netbeans.spi.editor.highlighting.ZOrder * @see org.netbeans.spi.editor.highlighting.HighlightsContainer */ public static HighlightsLayer create( String layerTypeId, ZOrder zOrder, boolean fixedSize, HighlightsContainer container ) { return new HighlightsLayer(layerTypeId, zOrder, fixedSize, container); } private final String layerTypeId; private final ZOrder zOrder; private final boolean fixedSize; private HighlightsContainer container; private HighlightsLayerAccessor accessor; /** * Creates a new HighlightsLayer as a proxy to another * HighlightsContainer. * * @param layerTypeId The unique identifier of the new layer. This id is * used for identifying this layer among other layers that may be created * for the same Document and it is used for example for * the purpose of z-order. * @param zOrder The layer's z-order. * @param fixedSize Whether this layer defines any attributes * affecting text rendering including attributes that affect the size * of the view containing the rendered text. * @param container HighlightsContainer that should be used * as a contents of this layer. * * @see org.netbeans.spi.editor.highlighting.ZOrder */ private HighlightsLayer( String layerTypeId, ZOrder zOrder, boolean fixedSize, HighlightsContainer container ) { assert layerTypeId != null : "The layerId parameter must not be null."; assert zOrder != null : "The zOrder parameter must not be null."; this.layerTypeId = layerTypeId; this.zOrder = zOrder; this.fixedSize = fixedSize; this.container = container; } /** * Gets the unique identifier of this layer. * * @return The layer id. * @see org.netbeans.spi.editor.highlighting.ZOrder */ /* package */ String getLayerTypeId() { return layerTypeId; } /** * Gets the z-order of this layer. The z-order defines a relative position * of this layer among other layers participating in drawing the same * document. The higher the z-order the higher this layer will be placed * and the 'more visible' its highlights will be. * * @return The layer's z-order. */ /* package */ ZOrder getZOrder() { return zOrder; } /** * Specifies whether highlights from this layer affect size of the rendered text. * *

If this layer provides highlights that change the size * of the view containing the rendered text (e.g. the font size) the infrastructure * needs to know about it and use the layer for laying out the views. Such * a layer should return true from this method. * *

Layers at the top (according to z-order) with highlights * that do not change metrics can be skipped during the views creation phase * and used later when drawing is done. * * @return false if the layer does not provide any highlights * that would alter the size of the text; otherwise true. */ /* package */ boolean isFixedSize() { return fixedSize; } /* package */ HighlightsContainer getContainer() { return container; } static { HighlightingSpiPackageAccessor.register(new PackageAccessor()); } private static final class PackageAccessor extends HighlightingSpiPackageAccessor { /** Creates a new instance of PackageAccessor */ public PackageAccessor() { } public HighlightsLayerFactory.Context createFactoryContext(Document document, JTextComponent component) { return new HighlightsLayerFactory.Context(document, component); } public List sort(Collection layers) throws TopologicalSortException { return ZOrder.sort(layers); } public HighlightsLayerAccessor getHighlightsLayerAccessor(final HighlightsLayer layer) { if (layer.accessor == null) { layer.accessor = new HighlightsLayerAccessor() { public String getLayerTypeId() { return layer.getLayerTypeId(); } public boolean isFixedSize() { return layer.isFixedSize(); } public ZOrder getZOrder() { return layer.getZOrder(); } public HighlightsContainer getContainer() { return layer.getContainer(); } }; } return layer.accessor; } public List dismantle(AttributeSet set) { return AttributesUtilities.dismantle(set); } } // End of PackageAccessor class } File [added]: HighlightsLayerFactory.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsLayerFactory.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 90 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import javax.swing.text.Document; import javax.swing.text.JTextComponent; /** * Factory for producing HighlightsLayers. Modules can implement * this interface and register their implementations among the mime-type specific * services under the Editors folder on the system filesystem. * *

The infrastructure uses HighlightsLayerFactory instances * registered in the mime-type specific registry to create HighlightsLayers, * which will participate in rendering a document. All factories that the infrastructure * considers relelvant for rendering a document will be asked to create * HighlightsLayers for that document. * * @author Miloslav Metelka * @author Vita Stejskal * @version 1.00 */ public interface HighlightsLayerFactory { /** * The context passed to a factory when it is asked to create HighlightLayers. * This context provides essential information such as a Document and * JTextComponent for which HighlightLayers should be created. */ public static final class Context { private Document document; private JTextComponent component; /** * TODO: we might need a MimePath here as well. Remember the layers can * be created/discarded depending on the appearance of embedded mime types * as the user changes the document. Such a layer (or the factory) might be interested in * knowing its mime-path, because it might need to load some settings at * the creation time when no other information (eg. lexer's language path) * is available. */ /* package */ Context(Document document, JTextComponent component) { this.document = document; this.component = component; } /** * Gets the document for which the highlight layers are created. */ public Document getDocument() { return document; } /** * Gets the text component for which the highlight layers are created. */ public JTextComponent getComponent() { return component; } } // End of Context class /** * Creates HighlightLayers appropriate for the context passed in. * * @param context The context that should be used for creating the layer. * * @return The array of HighlightLayers that should be used * for rendering the document in the Context. This method can * return null if there are no HighlightLayers available. */ HighlightsLayer[] createLayers(Context context); } File [added]: HighlightsSequence.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/HighlightsSequence.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 107 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.NoSuchElementException; import javax.swing.text.AttributeSet; import javax.swing.text.Position; /** * An iterator through highlights in a HighlightsContainer. * *

Implementation: Any HighlightsSequence obtained from any of the classes in * the Highlighting API will behave as so called fast-fail iterator. It * means that it will throw ConcurrentModificationException from * its methods if the underlying data (highlights) have changed since when the instance * of the HighlightsSequence was obtained. * * @author Miloslav Metelka * @version 1.00 */ public interface HighlightsSequence { /** * An empty HighlightsSequence. */ public static final HighlightsSequence EMPTY = new HighlightsSequence() { public boolean moveNext() { return false; } public int getStartOffset() { throw new NoSuchElementException(); } public int getEndOffset() { throw new NoSuchElementException(); } public AttributeSet getAttributes() { throw new NoSuchElementException(); } }; // End of EMPTY HighlightsSequence /** * Moves the internal pointer to the next highlight in this sequence (if there is any). * If this method returns true highlight's boundaries and attributes * can be retrieved by calling the getter methods. * * @return true If there is a highlight available and it is safe * to call the getters. * @throws ConcurrentModificationException If the highlights this sequence is * iterating through have been changed since the creation of the sequence. */ boolean moveNext(); /** * Gets the start offset of a current highlight. * * @return The offset in a document where the current highlight starts. * @throws ConcurrentModificationException If the highlights this sequence is * iterating through have been changed since the creation of the sequence. */ int getStartOffset(); /** * Gets the end offset of a current highlight. * * @return The offset in a document where the current highlight ends. * @throws ConcurrentModificationException If the highlights this sequence is * iterating through have been changed since the creation of the sequence. */ int getEndOffset(); /** * Gets the set of attributes that define how to render a current highlight. * *

Since the AttributeSet can contain any attributes implementors * must be aware of whether the attributes returned from this method affect * metrics or not and set the isFixedSize parameter appropriately * when createing HighlightsLayers. * * @return The set of text rendering attributes. Must not return null. * @throws ConcurrentModificationException If the highlights this sequence is * iterating through have been changed since the creation of the sequence. * * @see org.netbeans.spi.editor.highlighting.HighlightsLayer */ AttributeSet getAttributes(); } File [added]: ZOrder.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/ZOrder.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 282 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.logging.Logger; import org.openide.util.TopologicalSortException; import org.openide.util.Utilities; /** * This class determines a position of a HighlightsLayer in relation * to other layers. Instances of this class are immutable. * *

For the purpose of vertical ordering each HighlightsLayer is * identified by its layer type id. When creating a new * ZOrder you simply specify that the new ZOrder lies * above and/or below certain layers using their IDs. * *

Developers are encouraged to use the predefined z-order constants in this * class. Each constant refers to a rack in vertical ordering with a specific * purpose. The racks are ordered in the following way: * *

    *
  • TOP_RACK - the highest rack *
  • SHOW_OFF_RACK *
  • DEFAULT_RACK *
  • CARET_RACK *
  • SYNTAX_RACK *
  • BOTTOM_RACK - the lowest rack *
* *
*

When positioning your layer you should choose the rack that suites the purpose * of your layer. It is possible to further define more precise ordering * between layers within a rack. For example, if you have two layers - one providing * syntactical highlighting and the other one providing semantical highlighting - * they both belong to the SYNTAX_RACK, but the semantical layer should be placed * above the syntactical one, because it provides 'more accurate' highlights. You could * define ZOrder of your layers like this: * *

 * ZOrder syntaxLayerZOrder = ZOrder.SYNTAX;
 * ZOrder semanticLayerZOrder = ZOrder.SYNTAX.aboveLayers("your-syntax-layer-id");
 * 
*
* * @author Vita Stejskal */ public final class ZOrder { private static final Logger LOG = Logger.getLogger(ZOrder.class.getName()); private static final Collection EMPTY = new HashSet(); private static final String TOP_RACK_MARKER = "org-netbeans-spi-editor-highlighting-ZOrder-TOP-RACK"; //NOI18N private static final String SHOW_OFF_RACK_MARKER = "org-netbeans-spi-editor-highlighting-ZOrder-SHOW-OFF-RACK"; //NOI18N private static final String DEFAULT_RACK_MARKER = "org-netbeans-spi-editor-highlighting-ZOrder-DEFAULT-RACK"; //NOI18N private static final String CARET_RACK_MARKER = "org-netbeans-spi-editor-highlighting-ZOrder-CARET-RACK"; //NOI18N private static final String SYNTAX_RACK_MARKER = "org-netbeans-spi-editor-highlighting-ZOrder-SYNTAX-RACK"; //NOI18N /** * The highest rack of z-orders. Layers in this rack will be placed at * the top of the hierarchy. */ public static final ZOrder TOP_RACK = ZOrder.above(TOP_RACK_MARKER); /** * The show off rack of z-orders. This rack is meant to be used by * layers with short-lived highlights that can temporarily override highlights * provided by other layers (eg. syntax coloring). */ public static final ZOrder SHOW_OFF_RACK = new ZOrder( Collections.singleton(TOP_RACK_MARKER), Collections.singleton(SHOW_OFF_RACK_MARKER)); /** * The default rack of z-orders. This rack should be used by most of the layers. */ public static final ZOrder DEFAULT_RACK = new ZOrder( Collections.singleton(SHOW_OFF_RACK_MARKER), Collections.singleton(DEFAULT_RACK_MARKER)); public static final ZOrder CARET_RACK = new ZOrder( Collections.singleton(DEFAULT_RACK_MARKER), Collections.singleton(CARET_RACK_MARKER)); /** * The syntax highlighting rack of z-order. This rack is meant to be used by * layers that provide highlighting of a text according to its syntactical or * semantical rules. */ public static final ZOrder SYNTAX_RACK = new ZOrder( Collections.singleton(CARET_RACK_MARKER), Collections.singleton(SYNTAX_RACK_MARKER)); /** * The lowest rack of z-orders. Layers in this rack will be placed at the * bottom of the hierarchy. */ public static final ZOrder BOTTOM_RACK = ZOrder.below(SYNTAX_RACK_MARKER); /** * Creates a z-order that determines a position above the layers passed in. * * @param layerIds The IDs of layers which lay below the position that * will be refered to by a z-order created by this * method. * * @return The new z-order. */ public static ZOrder above(String... layerIds) { return new ZOrder(null, Arrays.asList(layerIds)); } /** * Creates a z-order that determines a position below the layers passed in. * * @param layerIds The IDs of layers which lay above the position that * will be refered to by a z-order created by this * method. * * @return The new z-order. */ public static ZOrder below(String... layerIds) { return new ZOrder(Arrays.asList(layerIds), null); } /** * Sorts an array of HighlightLayers by their z-order. This is * a convenience method that delegates to the sort(Collection) * method. * * @param layers The array to sort. * * @return The sorted array where layers are sorted by their z-order starting * with the lowest z-order and going to the highest one. * @throws TopologicalSortException If the array contains cycles. */ /* package */ static HighlightsLayer[] sort(HighlightsLayer[] layers) throws TopologicalSortException { List list = sort(Arrays.asList(layers)); return list.toArray(new HighlightsLayer [list.size()]); } /** * Sorts a collection of HighlightLayers by their z-order. The layers * with ZOrder.BOTTOM will preceed all other layers in the resulting * list. Similarily the layers with ZOrder.TOP will be placed at the * end of the list. All the other layers will appear in between sorted by their * z-order. * * @param layers The array to sort. * * @return The sorted array where layers are sorted by their z-order starting * with the lowest z-order and going to the highest one. * @throws TopologicalSortException If the collection contains cycles. */ /* package */ static List sort(Collection layers) throws TopologicalSortException { HashMap id2layer = new HashMap(); HashSet vertices = new HashSet(); HashMap> edges = new HashMap>(); vertices.add(TOP_RACK_MARKER); vertices.add(SHOW_OFF_RACK_MARKER); vertices.add(DEFAULT_RACK_MARKER); vertices.add(CARET_RACK_MARKER); vertices.add(SYNTAX_RACK_MARKER); edges.put(SYNTAX_RACK_MARKER, new ArrayList(Collections.singleton(CARET_RACK_MARKER))); edges.put(CARET_RACK_MARKER, new ArrayList(Collections.singleton(DEFAULT_RACK_MARKER))); edges.put(DEFAULT_RACK_MARKER, new ArrayList(Collections.singleton(SHOW_OFF_RACK_MARKER))); edges.put(SHOW_OFF_RACK_MARKER, new ArrayList(Collections.singleton(TOP_RACK_MARKER))); for (HighlightsLayer layer : layers) { id2layer.put(layer.getLayerTypeId(), layer); // process the layers below the current layer for (String belowLayerId : layer.getZOrder().layersBelow) { vertices.add(belowLayerId); List verticeEdges = edges.get(belowLayerId); if (verticeEdges == null) { verticeEdges = new ArrayList(); edges.put(belowLayerId, verticeEdges); } verticeEdges.add(layer.getLayerTypeId()); LOG.finest(belowLayerId + " < " + layer.getLayerTypeId()); } // process the layers above the current layer vertices.add(layer.getLayerTypeId()); List verticeEdges = edges.get(layer.getLayerTypeId()); if (verticeEdges == null) { verticeEdges = new ArrayList(); edges.put(layer.getLayerTypeId(), verticeEdges); } for (String aboveLayerId : layer.getZOrder().layersAbove) { verticeEdges.add(aboveLayerId); LOG.finest(layer.getLayerTypeId() + " < " + aboveLayerId); } } // Sort ordinary layers by their z-order List sortedLayerIds = Utilities.topologicalSort(vertices, edges); List sortedLayers = new ArrayList(); // Add all ordinary layers sorted by their z-order LOG.finest("Sorted layer Ids: "); for (String layerId : sortedLayerIds) { LOG.finest(" " + layerId); HighlightsLayer layer = id2layer.get(layerId); if (layer != null) { sortedLayers.add(layer); } } LOG.finest("End of Sorted layer Ids: -----------------------"); return sortedLayers; } /* package */ final Collection layersAbove; /* package */ final Collection layersBelow; /** Creates a new instance of ZOrder */ private ZOrder(Collection aboveLayers, Collection belowLayers) { this.layersAbove = aboveLayers == null ? EMPTY : aboveLayers; this.layersBelow = belowLayers == null ? EMPTY : belowLayers; } /** * Creates a copy of this ZOrder and modifies its position to lay above the * layers passed in. * * @param layerIds The IDs of layers which lay below the position that * will be refered to by a z-order created by this * method. * * @return The new z-order. */ public ZOrder aboveLayers(String... layerIds) { HashSet newLayersAbove = new HashSet(layersAbove); HashSet newLayersBelow = new HashSet(layersBelow.size() + layerIds.length); newLayersBelow.addAll(layersBelow); newLayersBelow.addAll(Arrays.asList(layerIds)); return new ZOrder(newLayersAbove, newLayersBelow); } /** * Creates a copy of this ZOrder and modifies its position to lay below the * layers passed in. * * @param layerIds The IDs of layers which lay above the position that * will be refered to by a z-order created by this * method. * * @return The new z-order. */ public ZOrder belowLayers(String... layerIds) { HashSet newLayersBelow = new HashSet(layersBelow); HashSet newLayersAbove = new HashSet(layersAbove.size() + layerIds.length); newLayersAbove.addAll(layersAbove); newLayersAbove.addAll(Arrays.asList(layerIds)); return new ZOrder(newLayersAbove, newLayersBelow); } } File [added]: package.html Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/package.html?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 526 ---------------- org.netbeans.spi.editor.lib2.highlighting

The Highlighting SPI is a new way of influencing how text in an editor component is rendered. The editor framework in Netbeans is an extension of the Swing Text SPI framework and as such it uses things like Elements and Views to render a Document on a screen.

Since the editor framework is primarily designed to support various different types of files in the IDE it has to give module a chance to participate in documents rendering. Modules providing support for different languages usually need to influence colors and fonts of different parts of a source file depending on what code it contains (i.e. syntax coloring) or what other information the module needs presenting to a user (e.g. text annotations, hyperlinking, etc.). This all and more can be achieved by using the Highlighting SPI.

Key parts of the SPI

The very basic idea behind the SPI is to render a document as a sandwich of independent layers, which will say what colors and font should be used for rendering particular parts of the document. These parts of the document together with their rendering attributes (i.e. colors or font) are called highlighted areas or highlights. Each layer can provide as many non-overlapping highlights as it likes and each module can provide as many layers as it needs. The implementation behind the SPI will collect all layers registered for a particular document type (i.e. mime type), ask each of them for its highlights, merge those highlights together and finally send them to the draw engine, which will render the document.

The whole SPI is organized around the HighlightsLayer class, which is the ultimate thing that modules need to implement in order to provide a list of highlights for a document. The HighlightsLayers are created by HighlightsLayerFactory, which should be registered in MimeLookup under the mime-type of a document that the layer should be used for. All layers registered for one type of a document are ordedred according to the ZOrder they provide. Besides of ZOrder the layers provide additional information about nature of highlights they maintain.

The HighlightsLayer class implements HighlightsContainer interface, which is the fundamental part of the SPI. The HighlightsContainer interface allows to get a list of highlighs and to listen on changes in highlights that it contains. Besides of HighlightsLayer there are two other implementations of this interface and they are OffsetsBag and PositionsBag. Both OffsetsBag and PositionsBag classes allow adding and removing highlights dynamically. The highlights can be added either one-by-one or in chunks; each change is reported to listeners.

HighlightsLayer registration

The registration of HighlightsLayers has to be done through an instance of the HighlightsLayerFactory class. The factory should be registered in MimeLookup under the mime-type of documents, which the HighlightsLayer should be used for. For example, if a module wants to provide HighlightsLayer for text/x-something documents it should implement its own HighlightsLayerFactory (e.g. org.some.module.HLFactory class) and register it in MimeLookup using its XML layer as it is shown on the example below.

<folder name="Editors">
  <folder name="text">
    <folder name="x-something">
        <file name="org-some-module-HLFactory.instance" />
    </folder>
  </folder>
</folder>
  

The HLFactory class will simply return a new instance of the module's implementation of the HighlightsLayer class from its createLayers method. The parameter of the createLayers method provides access to a JTextComponent and its Document, which the layer is being created for. The method can create and return multiple HighlightsLayers.

HighlightsLayer lifecycle

The lifecycle of HighlightsLayers is tied to the lifecycle of Document. The infrastructure creates new instances of layers by calling registered HighlightsLayerFactory objects every time it needs to visualize a new Document. The layers created for one Document are not cached or resused in any way. This means that the layers themselvs do not have to take care about a potential change of a Document instance in JTextComponent. The infrastructre will always create a new set of layers if the Document instance changes. Therefore the layers can simply hold their instance of JTextComponent and/or Document and treat them as invariants.

Locking and Document changes

The basics of the locking and events model of Swing documents is described in the javadoc of the javax.swing.text.AbstractDocument class. Netbeans documents use the same patterns and so does the Highlighting SPI, because of its tight relation to documents. The fundamentals of the Swing documents locking model are that any changes to a document are done under the document's write lock, the document's listeners are notified synchronously on the mutating thread and have full read access to the document, but can't modify it.

The main functionality of the Highlighting SPI is to maintain highlights of certain areas of a document. These highlights are specified as a triple of starting offset, ending offset and a set of attributes. The offsets are usually passed in and out accross the SPI boundaries in form of ints and even though some implementations (e.g. PositionsBag) use Positions the esential rule is that any calls in and out from the SPI have to be made under the document's read lock. Let's have a look on a few examples demonstrating what this means.

  • Any calls from the infrastructure to HighlightsLayer.getHighlights() have to be carried on under the read lock of the document, which the HighlightsLayer was created for.
  • When HighlightsLayer needs to notify its listeneres that some of its highlights have changed all the events have to be fired under the layer's document's read lock. Obviously, the listeners are not allowed to modify the document from the event notification methods.
  • Any calls to OffsetsBag or PositionsBag that modify their content have to be done under the read lock of the document, which the bag was created for. Both bags makes all the changes synchronously on the caller's thread and this even includes firing notification events. Therefore all events fired from OffsetsBag or PositionsBag will be fired under the document's read lock if the mutating thread holds the read lock.

The Highlighting SPI does not use any special threads and any processing it does is always done on the caller's thread. This means that the above described constraints hardly cause any limitation for practical use. The majority of things happening around a document are done from within DocumentListeners, which hold the document's read lock anyway.

The Highlighting SPI is generally thread-safe meaning that any implementation behind the SPI can be used simultaneously from multiple threads if not stated otherwise. This doesn't change in any way the rule of acquiring a read lock before calling the SPI. Swing documents generally allow access for multiple readers that can run concurrently.

Z-order

Since there can be multiple layers suplying highlights for one document and the highlights can generally overlap it is important to sort the layers according to their Z-order. For this purpose each layer has to supply appropriate ZOrder and its unique identification when it is constructed.

The layers are identified by their layer type id, which is simply a String uniquely identifying layers of the same type. An example of a layer type can be a 'syntax highlighting' layer. Since this layer can be implemented using the new Lexer framework the same implementation can be used for many types of a document (e.g. java files, properties, manifests, etc.). This means that there is likely to be many instances of the 'syntax highlighting' layer - one for each opened document - and each of those instances will have the same layer type id and ZOrder positioning it among the other layers created for that particular document.

ZOrder maintains a position of a layer relatively to other layers by keeping a list of ids of layers laying above the ZOrder's layer and a list of layer ids laying below the layer. Instances of the ZOrder class are immutable making it impossible to dynamically change a position of a layer in the z-order stack created for a document. There is a couple of static methods allowing creation of an instance of ZOrder and also methods for creating an instance of ZOrder based on another instance.

The ZOrder class contains several predefined z-order constants, which can be used as well-known positions. These constants are called z-order racks and are meant to be used as a starting point for positioning a layer. The racks are listed below in their respective z-order.

  • TOP_RACK - the top most rack
  • SHOW_OFF_RACK - layers providing short-lived highlights that can temporarily override highlights from other layers. An example can be text selection or text search layers.
  • DEFAULT_RACK - the rack for general layers
  • SYNTAX_RACK - layers providing syntax or semantic highlighting of text
  • BOTOM_RACK - the bottom rack

Using AttributeSet

The Highlighting SPI uses javax.swing.text.AttributeSet to define attributes for particular highlights. These attributes can be anything, which the editor's drawing engine understands and can render. Usually the attribute names are constants from javax.swing.StyleConstants or org.netbeans.api.editor.settings.EditorStyleConstants. The values depend on the meaning of each particular attribute, but they usually are instances of java.awt.Color, java.lang.Integer, Boolean.TRUE or Boolean.FALSE and similar.

Since there can be more highlighting layers participating on one document and they can provide highlights that overlap the infrastructure will merge attributes from all AttributeSets provided for areas with overlapping highlights. The merging is done in the order defined by ZOrders of the participating layers, which means that if two layers provide an attribute with the same name then the merged AttributeSet will contain the attribute from the layer, which is placed higher in the z-order hierarchy.

There are two important rules for using AttributeSets, which should be carefully followed by all highlighting layer implementations. Violating these rules may potentialy break the rendering of a document or may cause performance problems.

  • Immutability - All instances of AttributeSets should always be treated as immutable objects. Once you create an AttibuteSet and use it for a highlight you should not modify it. Your modification is most likely to be ignored or can have unpredictable results.
  • One instance only - There can be thousands of highlights supplied by layers for one document and a lot of them will be the same (e.g. all keywords in a java document are highlighted with the same color and font). It would be unreasonable to create a new AttributeSet for each of those highlights when all of them would in fact be the same. Instead you should always create one instance of AttributeSet and share it among all highlights that render the same text category (e.g. token or token category, etc.).

The AttributeSets used for highlighting are often created by calling FontColorSettings and it is a responsibility of this class to prevent excesive creation of AttributeSets it provides. However, if your highlighting layer creates its own AttributeSets they should always be cached and reused. You can use methods from the AttributesUtilities class for creating immutable AttributeSets.

Use cases

Use case 1. - Caret selection

The Netbeans editor as any other modern editor allows selecting blocks of text and highlighting them to a user for easier identification. We call this functionality caret selection services and it includes things as simple as marking a block of text that the user selected for copy/paste operation or highlighting a line where the caret is placed to more complex ones such as highlighting occurences of a text that the user search for using the 'Find dialog', etc.

This functionality usually only needs to create one highlight and update it depending on the caret movements/selection notified from JTextComponent. The more complex cases may need to create several highlights (e.g. to show the text being searched for). Generally, the highlights are created independently on the text changes in the document itself (e.g. the caret move or searching for a text). However, they have to survive editing the document (e.g. the highlighted occurences of the searched text have to remain highlighted when other parts of the document are edited).

The caret selection highlights are generally short-lived and have higher importance than other highlights (e.g. syntax or semantic coloring). They usually change the background color to highlight the selection, but also retain as much of a visual appearance of the highlighted text as possible.

Use case 2. - Syntax highlighting

This type of a document coloring shows 'words' or characters in different colors to indicate their meaning in the structure of the text document. This is very popular with highly structured documents such as source code files, scripts, SGML-like documents, etc. It's usually not used for plain text documents containing text in a human language.

Syntax highlighting in Netbeans editor is based on a lexical analysis done by lexer plug-ins registered for various types of documents. The lexers are written using the APIs provided by the Lexer module. During the lexical analysis text gets split into tokens of different types and categories. Each token type or category can have defined its own coloring information such as font and foreground and background colors, etc. Tokens know their position in text (i.e. offset and length), which information can then be used for creating highlights.

Decoupling the lexers and making them pluggable lets the syntax highlighting be very flexible. A single layer based on the Lexer API can colorify all sorts of documents providing that there is a lexer registered for each type of a document.

Generally a syntax analysis is very fast and syntax highlighting immediately reflects changes done in text. The syntax highlighting layer is usually at the bottom of the hierarchy of highlighting layers.

Use case 3. - Semantic highlighting

In fact semantic coloring regardless of the language it is provided for is very similar to syntax coloring. Words or groups of characters are highlighted depending on their meaning in the text. The difference is in the amount of information that is needed to make this type of coloring meaningful. While with syntax coloring all the information needed is in the text itself in semantic coloring parts of text can be colored depending on information found in a completely different document (e.g. in another source file, library, project, etc.).

Semantic highlighting is highly dependent on the type of a document and therefore is usually provided on case-by-case basis and only for the most important types of documents (i.e. those most frequenty used such as java files in Netbeans). Also, semantic coloring is generally not very fast, because of the amount of information that is sometimes needed to gather before a document can be colored. Therefore, while all the effort is made to make semantic coloring reflect text changes as soon as possible, it is generally done asynchronously outside of the documents event model and highlights are created as soon as they are available. The tokens created during the semantic analysis always contain token's position within the text in some form (i.e. either offset or Position). If Positions are available they should be accepted and re-used by the Highlighting SPI.

Use case 4. - Embedded languages

An embedded language is a language of a part of a document that is different than the main language of the document. An example can be a java scriplet in a JSP page or JavaScript in an HTML document. The main language of a JSP page is 'text/x-jsp' and the emebedded language in the case of a java scriplet is 'text/x-java'. For the HTML document the main language is 'text/html' and if a JavaScript part is included in the document the 'text/x-javascript' is the embedded language.

The language embedding is supported by Lexer API and therefore there is no problem with supporting it for syntax coloring. For semantic coloring all the work lies on the highlighting layers providing semantic coloring support for a particular language. These layers have to be prepared to provide highlights for parts of a document, which does not contain text in the language they support, but which contains some embedded parts in that language. The Highlighting infrastructure will scan the document for all languages it contains and then it will create appropriate highlighting layers. The layers can be added dynamically as user inserts parts of text in a new language. The layers, however, may not be removed immediately when the last part of text in a language they suppor is removed. Therefore the layers should be prepared to provide no highlights if there is no text they recognize.

Use case 5. - Filtering layers used for JTextComponent

In certain situations JTextComponent or JEditorPane are used for other purposes than editing. For example debugger may want to show JEditorPane for adding a new watch, where a user could write a piece of java code that should evaluated. This pane should use basically the same layers so that the entered code looks like properly colored and formatted java code. However, it is not desirable to use exactly the same layers as for an ordinary java editor, because some highlightings have a little value in this context or could even be disturbing. There is no point in highlighting the row with the caret, because watches are essentially one-line expressions. There is also a little point in showing text-search related highlights, because hardly anybody will use text search in these simple expressions anyway. On the other hand it makes sense to highlight selected text if user selects some.

There can be a whole range of usecases where modules need to show an editor pane, but do not want to use a particular set of highlighting layers, which are registered for the mime type of text that the module is trying to display and which would normally be used for an ordinary editor pane. These usecases are very specific for each module and its way of implementing some features.

The editor insfrastructure supports this usecase through allowing modules to set special properties on the editor pane that they want to use for displaying text. The properties are called HighlightsLayerIncludes and HighlightsLayerExcludes. The value of those properties can be String or String [] of regular expressions that will be used for finding the matching layers by evaluating each regular expression against the layer's type id. The exact interpretation of those two properties is described below.

  • HighlightsLayerIncludes - Defines the set of layers that will be used for rendering text in an editor pane that defines this property. Every layer, which type Id matches at least one of the regular expressions defined by this property, will be included for rendering. The default value is null, which means that all registered layers will be used.
  • HighlightsLayerExcludes - Defines the set of layers that will not be used for rendering. Every layer, which type Id matches at least one of the regular expressions defined by this property, will be excluded from rendering. The default value is null, which means that no layer will be excluded.

The filters defined by those two properties are used in the same order as they were listed above. That is the includes are used first and whatever layers they includ are then filtered by the excludes filter. The result is then used for rendering text in an editor component, which defined those properties.

The example below shows how to disable the caret row highlighting layer on JEditorPane.

  JEditorPane pane = new JEditorPane();
  pane.putClientProperty(
    "HighlightsLayerExcludes", 
    "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"
  );
  

Other usecases

The main usecases described above are certainly not the only usecases of the Highlighting SPI. In general the SPI can be used for binding any type of information to parts of text in a document. While this information should have limited size to keep a good performance of Netbeans editor it can be pretty much anything. Information provided in highlights is currently used only by the editor's drawing engine, which provides a limited set of features useful mostly for rendering text. Some other uses could be for example text annotations, hyperlinking, showing icons in text, etc.

Directory: /editor/lib2/src/org/netbeans/spi/editor/highlighting/support/ ========================================================================= File [added]: AbstractHighlightsContainer.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/support/AbstractHighlightsContainer.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 92 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.List; import org.netbeans.lib.editor.util.ListenerList; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsSequence; /** * The default implementation of the HighlightsContainer interface. * It provides standard implementation of the methods for adding and removing * HighlightsChangeListeners and allows subclasses to notify listeners * by calling the fireHighlightsChange method. * * @author Vita Stejskal */ public abstract class AbstractHighlightsContainer implements HighlightsContainer { private ListenerList listeners = new ListenerList(); /** Creates a new instance of AbstractHighlightsContainer */ protected AbstractHighlightsContainer() { } public abstract HighlightsSequence getHighlights(int startOffset, int endOffset); /** * Adds HighlightsChangeListener to this container. * * @param listener The listener to add. */ public final void addHighlightsChangeListener(HighlightsChangeListener listener) { synchronized (listeners) { listeners.add(listener); } } /** * Removes HighlightsChangeListener to this container. * * @param listener The listener to remove. */ public final void removeHighlightsChangeListener(HighlightsChangeListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * Notifies all registered listeners about a change in this container. The * area of a document where highlights changed is specified by the * changeStartOffset and changeEndOffset parameters. * * @param changeStartOffset The starting offset of the changed area. * @param changeEndOffset The ending offset of the changed area. */ protected final void fireHighlightsChange(int changeStartOffset, int changeEndOffset) { List targets; synchronized (listeners) { targets = listeners.getListeners(); } if (targets.size() > 0) { HighlightsChangeEvent evt = new HighlightsChangeEvent(this, changeStartOffset, changeEndOffset); for(HighlightsChangeListener l : targets) { l.highlightChanged(evt); } } } } File [added]: OffsetsBag.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/support/OffsetsBag.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 719 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.lang.ref.WeakReference; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import org.netbeans.modules.editor.lib2.highlighting.OffsetGapList; import org.netbeans.spi.editor.highlighting.*; import org.openide.util.Utilities; /** * A bag of highlighted areas specified by their offsets in a document. * *

The highlighted areas (highlights) are determined by their starting and ending * offsets in a document and the set of attributes that should be used for rendering * that area. The highlights can be arbitrarily added to and remove from this bag. * *

The OffsetsBag is designed to work over a single * document, which is passed in to the constructor. All offsets * accepted by various methods in this class must refer to positions within * this document. Therefore any offsets passed in to the methods in this class * have to be equal to or greater than zero and less than or equal to the document * size. * *

The OffsetsBag can operate in two modes depending on a * value passed in its construtor. The mode determines how the bag will treat * newly added highlights that overlap with existing highlights in the bag. * *

Trimming mode: * In the trimming mode the bag will trim any existing highlights that * would overlap with a newly added highlight. In this mode the newly * added highlights always replace the existing highlights if they overlap. * *

Merging mode: * In the merging mode the bag will merge attributes of the overlapping highlights. * The area where the new highlight overlaps with some existing highlights will * then constitute a new highlight, which attributes will be a composition of * the attributes of both the new and existing highlight. Should there be attributes * with the same name the attribute values from the newly added highlight will take * precedence. * * @author Vita Stejskal */ public final class OffsetsBag extends AbstractHighlightsContainer { private static final Logger LOG = Logger.getLogger(OffsetsBag.class.getName()); private Document document; private OffsetGapList marks; private boolean mergeHighlights; private long version = 0; private DocL docListener; /** * Creates a new instance of OffsetsBag, which trims highlights * as they are added. It calls the {@link #OffsetsBag(Document, boolean)} constructor * passing false as a parameter. * * @param document The document that should be highlighted. */ public OffsetsBag(Document document) { this(document, false); } /** * Creates a new instance of OffsetsBag. * * @param document The document that should be highlighted. * @param mergeHighlights Determines whether highlights should be merged * or trimmed. */ public OffsetsBag(Document document, boolean mergeHighlights) { this.document = document; this.mergeHighlights = mergeHighlights; this.marks = new OffsetGapList(); this.docListener = new DocL(this); this.document.addDocumentListener(docListener); } /** * Adds a highlight to this bag. The highlight is specified by its staring * and ending offset and by its attributes. Adding a highlight that overlaps * with one or more existing highlights can have a different result depending * on the value of the mergingHighlights parameter used for * constructing this bag. * * @param startOffset The beginning of the highlighted area. * @param endOffset The end of the highlighted area. * @param attributes The attributes to use for highlighting. */ public void addHighlight(int startOffset, int endOffset, AttributeSet attributes) { int [] offsets; synchronized (marks) { offsets = addHighlightImpl(startOffset, endOffset, attributes); if (offsets != null) { version++; } } if (offsets != null) { fireHighlightsChange(offsets[0], offsets[1]); } } /** * Adds all highlights from the bag passed in. This method is equivalent * to calling addHighlight for all the highlights in the * bag except that the changes are done atomically. * * @param bag The bag of highlights that will be atomically * added to this bag. */ public void addAllHighlights(HighlightsSequence bag) { int [] offsets; synchronized (marks) { offsets = addAllHighlightsImpl(bag); if (offsets != null) { version++; } } if (offsets != null) { fireHighlightsChange(offsets[0], offsets[1]); } } /** * Resets this bag to use the new set of highlights. This method drops * all the existing highlights in this bag and adds all highlights from * the sequence passed in as a parameter. The changes are made atomically. * The sequence passed in has to be acting on the same Document * as this bag. * * @param seq New highlights to add. */ public void setHighlights(HighlightsSequence seq) { if (seq instanceof Seq) { setHighlights(((Seq) seq).getBag()); return; } int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; synchronized (marks) { int [] clearedArea = clearImpl(); int [] populatedArea = addAllHighlightsImpl(seq); if (clearedArea != null) { changeStart = clearedArea[0]; changeEnd = clearedArea[1]; } if (populatedArea != null) { if (changeStart == Integer.MAX_VALUE || changeStart > populatedArea[0]) { changeStart = populatedArea[0]; } if (changeEnd == Integer.MIN_VALUE || changeEnd < populatedArea[1]) { changeEnd = populatedArea[1]; } } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Resets this bag to use the new set of highlights. This method drops * all the existing highlights in this bag and adds all highlights from * the bag passed in as a parameter. The changes are made atomically. Both * bags have to be acting on the same Document. * * @param bag New highlights to add. */ public void setHighlights(OffsetsBag bag) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; synchronized (marks) { int [] clearedArea = clearImpl(); int [] populatedArea = null; OffsetGapList newMarks = bag.getMarks(); synchronized (newMarks) { for(OffsetsBag.Mark mark : newMarks) { marks.add(marks.size(), new OffsetsBag.Mark(mark.getOffset(), mark.getAttributes())); } if (marks.size() > 0) { populatedArea = new int [] { marks.get(0).getOffset(), marks.get(marks.size() - 1).getOffset() }; } } if (clearedArea != null) { changeStart = clearedArea[0]; changeEnd = clearedArea[1]; } if (populatedArea != null) { if (changeStart == Integer.MAX_VALUE || changeStart > populatedArea[0]) { changeStart = populatedArea[0]; } if (changeEnd == Integer.MIN_VALUE || changeEnd < populatedArea[1]) { changeEnd = populatedArea[1]; } } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Removes highlights in a specific area of the document. All existing highlights * that are positioned within the area specified by the startOffset and * endOffset parameters are removed from this bag. The highlights * that only partialy overlap with the area are treated according to the value * of the clip parameter. * *

    *
  • If clip == true : the overlapping highlights will remain * in this sequence but will be clipped so that they do not overlap anymore *
  • If clip == false : the overlapping highlights will be * removed from this sequence *
* * @param startOffset The beginning of the area to clear. * @param endOffset The end of the area to clear. */ public void removeHighlights(int startOffset, int endOffset, boolean clip) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; // Ignore empty areas if (startOffset == endOffset) { return; } else { assert startOffset < endOffset : "Start offset must be before the end offset. startOffset = " + startOffset + ", endOffset = " + endOffset; //NOI18N } synchronized (marks) { if (marks.isEmpty()) { return; } int startIdx = indexBeforeOffset(startOffset); int endIdx = indexBeforeOffset(endOffset, startIdx < 0 ? 0 : startIdx, marks.size() - 1); // System.out.println("removeHighlights(" + startOffset + ", " + endOffset + ", " + clip + ") : startIdx = " + startIdx + ", endIdx = " + endIdx); if (clip) { if (startIdx == endIdx) { if (startIdx != -1 && marks.get(startIdx).getAttributes() != null) { AttributeSet original = marks.get(startIdx).getAttributes(); if (marks.get(startIdx).getOffset() == startOffset) { marks.set(startIdx, new Mark(endOffset, original)); } else { marks.add(startIdx + 1, new Mark(startOffset, null)); marks.add(startIdx + 2, new Mark(endOffset, original)); } changeStart = startOffset; changeEnd = endOffset; } // make sure nothing gets removed startIdx = Integer.MAX_VALUE; endIdx = Integer.MIN_VALUE; } else { assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx; if (marks.get(endIdx).getAttributes() != null) { marks.set(endIdx, new Mark(endOffset, marks.get(endIdx).getAttributes())); changeEnd = endOffset; endIdx--; } if (startIdx != -1 && marks.get(startIdx).getAttributes() != null) { startIdx++; if (startIdx <= endIdx) { marks.set(startIdx, new Mark(startOffset, null)); } else { marks.add(startIdx, new Mark(startOffset, null)); } changeStart = startOffset; } startIdx++; } } else { if (startIdx == -1 || marks.get(startIdx).getAttributes() == null) { startIdx++; } if (endIdx != -1 && marks.get(endIdx).getAttributes() != null) { endIdx++; } } if (startIdx <= endIdx) { if (changeStart == Integer.MAX_VALUE) { changeStart = marks.get(startIdx).getOffset(); } if (changeEnd == Integer.MIN_VALUE) { changeEnd = marks.get(endIdx).getOffset(); } marks.remove(startIdx, endIdx - startIdx + 1); } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Gets highlights from an area of a document. The HighlightsSequence is * computed using all the highlights present in this bag between the * startOffset and endOffset. * * @param startOffset The beginning of the area. * @param endOffset The end of the area. * * @return The HighlightsSequence which iterates through the * highlights in the given area of this bag. */ public HighlightsSequence getHighlights(int startOffset, int endOffset) { if (LOG.isLoggable(Level.FINE) && !(startOffset < endOffset)) { LOG.fine("startOffset must be less than endOffset: startOffset = " + //NOI18N startOffset + " endOffset = " + endOffset); //NOI18N } synchronized (marks) { return new Seq(version, startOffset, endOffset); } } /** * Removes all highlights previously added to this bag. */ public void clear() { int [] clearedArea; synchronized (marks) { clearedArea = clearImpl(); if (clearedArea != null) { version++; } } if (clearedArea != null) { fireHighlightsChange(clearedArea[0], clearedArea[1]); } } // ---------------------------------------------------------------------- // Package private API // ---------------------------------------------------------------------- /* package */ OffsetGapList getMarks() { return marks; } /* package */ Document getDocument() { return document; } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private int [] addHighlightImpl(int startOffset, int endOffset, AttributeSet attributes) { if (startOffset == endOffset) { return null; } else { assert startOffset < endOffset : "Start offset must be before the end offset. startOffset = " + startOffset + ", endOffset = " + endOffset; //NOI18N assert attributes != null : "Highlight attributes must not be null."; //NOI18N } if (mergeHighlights) { merge(startOffset, endOffset, attributes); } else { trim(startOffset, endOffset, attributes); } return new int [] { startOffset, endOffset }; } private void merge(int startOffset, int endOffset, AttributeSet attributes) { AttributeSet lastKnownAttributes = null; int startIdx = indexBeforeOffset(startOffset); if (startIdx < 0) { startIdx = 0; marks.add(startIdx, new Mark(startOffset, attributes)); } else { Mark mark = marks.get(startIdx); AttributeSet newAttribs = AttributesUtilities.createComposite(attributes, mark.getAttributes()); lastKnownAttributes = mark.getAttributes(); if (mark.getOffset() == startOffset) { mark.setAttributes(newAttribs); } else { startIdx++; marks.add(startIdx, new Mark(startOffset, newAttribs)); } } for(int idx = startIdx + 1; ; idx++) { if (idx < marks.size()) { Mark mark = marks.get(idx); if (mark.getOffset() < endOffset) { lastKnownAttributes = mark.getAttributes(); mark.setAttributes(AttributesUtilities.createComposite(attributes, lastKnownAttributes)); } else { if (mark.getOffset() > endOffset) { marks.add(idx, new Mark(endOffset, lastKnownAttributes)); } break; } } else { marks.add(idx, new Mark(endOffset, lastKnownAttributes)); break; } } } private void trim(int startOffset, int endOffset, AttributeSet attributes) { int startIdx = indexBeforeOffset(startOffset); int endIdx = indexBeforeOffset(endOffset, startIdx < 0 ? 0 : startIdx, marks.size() - 1); // System.out.println("trim(" + startOffset + ", " + endOffset + ") : startIdx = " + startIdx + ", endIdx = " + endIdx); if (startIdx == endIdx) { AttributeSet original = null; if (startIdx != -1 && marks.get(startIdx).getAttributes() != null) { original = marks.get(startIdx).getAttributes(); } if (startIdx != -1 && marks.get(startIdx).getOffset() == startOffset) { marks.get(startIdx).setAttributes(attributes); } else { startIdx++; marks.add(startIdx, new Mark(startOffset, attributes)); } startIdx++; marks.add(startIdx, new Mark(endOffset, original)); } else { assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx; //NOI81N marks.set(endIdx, new Mark(endOffset, marks.get(endIdx).getAttributes())); endIdx--; startIdx++; if (startIdx <= endIdx) { marks.set(startIdx, new Mark(startOffset, attributes)); } else { marks.add(startIdx, new Mark(startOffset, attributes)); } startIdx++; if (startIdx <= endIdx) { marks.remove(startIdx, endIdx - startIdx + 1); } } } private int [] addAllHighlightsImpl(HighlightsSequence sequence) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; for ( ; sequence.moveNext(); ) { addHighlightImpl(sequence.getStartOffset(), sequence.getEndOffset(), sequence.getAttributes()); if (changeStart == Integer.MAX_VALUE) { changeStart = sequence.getStartOffset(); } changeEnd = sequence.getEndOffset(); } if (changeStart != Integer.MAX_VALUE && changeEnd != Integer.MIN_VALUE) { return new int [] { changeStart, changeEnd }; } else { return null; } } private int [] clearImpl() { if (!marks.isEmpty()) { int changeStart = marks.get(0).getOffset(); int changeEnd = marks.get(marks.size() - 1).getOffset(); marks.clear(); return new int [] { changeStart, changeEnd }; } else { return null; } } private int indexBeforeOffset(int offset, int low, int high) { int idx = marks.findElementIndex(offset, low, high); if (idx < 0) { idx = -idx - 1; // the insertion point: <0, size()> return idx - 1; } else { return idx; } } private int indexBeforeOffset(int offset) { return indexBeforeOffset(offset, 0, marks.size() - 1); } /* package */ static final class Mark extends OffsetGapList.Offset { private AttributeSet attribs; public Mark(int offset, AttributeSet attribs) { super(offset); this.attribs = attribs; } public AttributeSet getAttributes() { return attribs; } public void setAttributes(AttributeSet attribs) { this.attribs = attribs; } } // End of Mark class private final class Seq implements HighlightsSequence { private long version; private int startOffset; private int endOffset; private int idx = -1; public Seq(long version, int startOffset, int endOffset) { this.version = version; this.startOffset = startOffset; this.endOffset = endOffset; } public boolean moveNext() { synchronized (OffsetsBag.this.marks) { checkVersion(); if (idx == -1) { idx = indexBeforeOffset(startOffset); if (idx == -1 && marks.size() > 0) { idx = 0; } } else { idx++; } while (isIndexValid(idx)) { if (marks.get(idx).getAttributes() != null) { return true; } // Skip any empty areas idx++; } return false; } } public int getStartOffset() { synchronized (OffsetsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return Math.max(marks.get(idx).getOffset(), startOffset); } else { throw new NoSuchElementException(); } } } public int getEndOffset() { synchronized (OffsetsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return Math.min(marks.get(idx + 1).getOffset(), endOffset); } else { throw new NoSuchElementException(); } } } public AttributeSet getAttributes() { synchronized (OffsetsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return marks.get(idx).getAttributes(); } else { throw new NoSuchElementException(); } } } private boolean isIndexValid(int idx) { return idx >= 0 && idx + 1 < marks.size() && marks.get(idx).getOffset() < endOffset && marks.get(idx + 1).getOffset() > startOffset; } private OffsetsBag getBag() { return OffsetsBag.this; } private void checkVersion() { if (OffsetsBag.this.version != version) { throw new ConcurrentModificationException(); } } } // End of Seq class private static final class DocL extends WeakReference implements DocumentListener, Runnable { private Document document; public DocL(OffsetsBag bag) { super(bag, Utilities.activeReferenceQueue()); this.document = bag.getDocument(); } public void insertUpdate(DocumentEvent e) { OffsetsBag bag = get(); if (bag != null) { synchronized (bag.marks) { bag.marks.defaultInsertUpdate(e.getOffset(), e.getLength()); } } else { run(); } } public void removeUpdate(DocumentEvent e) { OffsetsBag bag = get(); if (bag != null) { synchronized (bag.marks) { bag.marks.defaultRemoveUpdate(e.getOffset(), e.getLength()); } } else { run(); } } public void changedUpdate(DocumentEvent e) { // not interested } public void run() { Document d = document; if (d != null) { d.removeDocumentListener(this); document = null; } } } // End of DocL class } File [added]: PositionsBag.java Url: http://editor.netbeans.org/source/browse/editor/lib2/src/org/netbeans/spi/editor/highlighting/support/PositionsBag.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 719 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.Position; import org.netbeans.lib.editor.util.GapList; import org.netbeans.spi.editor.highlighting.*; /** * A bag of highlighted areas specified by their document Positions. * *

The highlighted areas (highlights) are determined by their starting and ending * positions in a document and the set of attributes that should be used for rendering * that area. The highlights can be arbitrarily added to and remove from the bag. * *

The PositionsBag is designed to work over a single * document, which is passed in to the constructor. All the Positions * accepted by various methods in this class must refer to positions within * this document. Since there is no way how this could be checked it is up to * the users of this class to make sure that Positions they pass in * to the bag are from the same Document, which they used for creating * the bag. * *

The PositionsBag can operate in two modes depending on a * value passed in its construtor. The mode determines how the bag will treat * newly added highlights that overlap with existing highlights in the bag. * *

Trimming mode: * In the trimming mode the bag will trim any existing highlights that * would overlap with a newly added highlight. In this mode the newly * added highlights always replace the existing highlights if they overlap. * *

Merging mode: * In the merging mode the bag will merge attributes of the overlapping highlights. * The area where the new highlight overlaps with some existing highlights will * then constitute a new highlight and its attributes will be a composition of * the attributes of both the new and existing highlight. Should there be attributes * with the same name the attribute values from the newly added highlight will take * precedence. * * @author Vita Stejskal */ public final class PositionsBag extends AbstractHighlightsContainer { private static final Logger LOG = Logger.getLogger(PositionsBag.class.getName()); private Document document; private boolean mergeHighlights; private GapList marks = new GapList(); private GapList attributes = new GapList(); private long version = 0; /** * Creates a new instance of PositionsBag, which trims highlights * as they are added. It calls the {@link #PositionsBag(Document, boolean)} constructore * passing false as a parameter. * * @param document The document that should be highlighted. */ public PositionsBag(Document document) { this(document, false); } /** * Creates a new instance of PositionsBag. * * @param document The document that should be highlighted. * @param mergeHighlights Determines whether highlights should be merged * or trimmed. */ public PositionsBag(Document document, boolean mergeHighlights) { this.document = document; this.mergeHighlights = mergeHighlights; } /** * Adds a highlight to this bag. The highlight is specified by its staring * and ending Position and by its attributes. Adding a highlight that overlaps * with one or more existing highlights can have a different result depending * on the value of the mergingHighlights parameter used for * constructing this bag. * * @param startPosition The beginning of the highlighted area. * @param endPosition The end of the highlighted area. * @param attributes The attributes to use for highlighting. */ public void addHighlight(Position startPosition, Position endPosition, AttributeSet attributes) { int [] offsets; synchronized (marks) { offsets = addHighlightImpl(startPosition, endPosition, attributes); if (offsets != null) { version++; } } if (offsets != null) { fireHighlightsChange(offsets[0], offsets[1]); } } /** * Adds all highlights from the bag passed in. This method is equivalent * to calling addHighlight for all the highlights in the * bag except that the changes are done atomically. * * @param bag The highlights that will be atomically added to this bag. */ public void addAllHighlights(PositionsBag bag) { int [] offsets; synchronized (marks) { offsets = addAllHighlightsImpl(bag); if (offsets != null) { version++; } } if (offsets != null) { fireHighlightsChange(offsets[0], offsets[1]); } } /** * Resets this sequence to use the new set of highlights. This method drops * all the existing highlights in this bag and adds all highlights from * the bag passed in as a parameter. The changes are done atomically. * * @param bag The new highlights. */ public void setHighlights(PositionsBag bag) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; synchronized (marks) { int [] clearedArea = clearImpl(); int [] populatedArea = null; GapList newMarks = bag.getMarks(); GapList newAttributes = bag.getAttributes(); synchronized (newMarks) { for(Position mark : newMarks) { marks.add(marks.size(), mark); } for(AttributeSet attrib : newAttributes) { attributes.add(attributes.size(), attrib); } if (marks.size() > 0) { populatedArea = new int [] { marks.get(0).getOffset(), marks.get(marks.size() - 1).getOffset() }; } } if (clearedArea != null) { changeStart = clearedArea[0]; changeEnd = clearedArea[1]; } if (populatedArea != null) { if (changeStart == Integer.MAX_VALUE || changeStart > populatedArea[0]) { changeStart = populatedArea[0]; } if (changeEnd == Integer.MIN_VALUE || changeEnd < populatedArea[1]) { changeEnd = populatedArea[1]; } } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Removes highlights in the specific area. All existing highlights * that are positioned within the area specified by the startOffset and * endOffset parameters are removed from this bag. The highlights * that only partialy overlap with the area are treated according to the value * of the clip parameter. * *

    *
  • If clip == true : the overlapping highlights will remain * in this sequence but will be clipped so that they do not overlap anymore *
  • If clip == false : the overlapping highlights will be * removed from this sequence *
* * @param startPosition The beginning of the area to clear. * @param endPosition The end of the area to clear. */ public void removeHighlights(Position startPosition, Position endPosition, boolean clip) { if (!clip) { removeHighlights(startPosition.getOffset(), endPosition.getOffset()); return; } int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; // Ignore empty areas if (startPosition.getOffset() == endPosition.getOffset()) { return; } else { assert startPosition.getOffset() < endPosition.getOffset() : "Start position must be before the end position"; //NOI18N } synchronized (marks) { if (marks.isEmpty()) { return; } int startIdx = indexBeforeOffset(startPosition.getOffset()); int endIdx = indexBeforeOffset(endPosition.getOffset(), startIdx < 0 ? 0 : startIdx, marks.size() - 1); // System.out.println("removeHighlights(" + startOffset + ", " + endOffset + ", " + clip + ") : startIdx = " + startIdx + ", endIdx = " + endIdx); if (startIdx == endIdx) { if (startIdx != -1 && attributes.get(startIdx) != null) { AttributeSet original = attributes.get(startIdx); if (marks.get(startIdx).getOffset() == startPosition.getOffset()) { marks.set(startIdx, endPosition); attributes.set(startIdx, original); } else { marks.add(startIdx + 1, startPosition); attributes.add(startIdx + 1, null); marks.add(startIdx + 2, endPosition); attributes.add(startIdx + 2, original); } changeStart = startPosition.getOffset(); changeEnd = endPosition.getOffset(); } } else { assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx; if (attributes.get(endIdx) != null) { marks.set(endIdx, endPosition); changeEnd = endPosition.getOffset(); endIdx--; } if (startIdx != -1 && attributes.get(startIdx) != null) { startIdx++; if (startIdx <= endIdx) { marks.set(startIdx, startPosition); attributes.set(startIdx, null); } else { marks.add(startIdx, startPosition); attributes.add(startIdx, null); } changeStart = startPosition.getOffset(); } startIdx++; if (startIdx <= endIdx) { if (changeStart == Integer.MAX_VALUE) { changeStart = marks.get(startIdx).getOffset(); } if (changeEnd == Integer.MIN_VALUE) { changeEnd = marks.get(endIdx).getOffset(); } marks.remove(startIdx, endIdx - startIdx + 1); attributes.remove(startIdx, endIdx - startIdx + 1); } } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Removes highlights in the specific area. All existing highlights * that are positioned within the area specified by the startOffset and * endOffset parameters are removed from this sequence. The highlights * that only partialy overlap with the area will be removed as well. * * @param startOffset The beginning of the area to clear. * @param endOffset The end of the area to clear. */ public void removeHighlights(int startOffset, int endOffset) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; // Ignore empty areas if (startOffset == endOffset) { return; } else { assert startOffset < endOffset : "Start position must be before the end position"; //NOI18N } synchronized (marks) { if (marks.isEmpty()) { return; } int startIdx = indexBeforeOffset(startOffset); int endIdx = indexBeforeOffset(endOffset, startIdx < 0 ? 0 : startIdx, marks.size() - 1); // System.out.println("removeHighlights(" + startOffset + ", " + endOffset + ", " + clip + ") : startIdx = " + startIdx + ", endIdx = " + endIdx); if (startIdx == -1 || attributes.get(startIdx) == null) { startIdx++; } if (endIdx != -1 && attributes.get(endIdx) != null) { endIdx++; } if (startIdx < endIdx) { if (changeStart == Integer.MAX_VALUE) { changeStart = marks.get(startIdx).getOffset(); } if (changeEnd == Integer.MIN_VALUE) { changeEnd = marks.get(endIdx).getOffset(); } marks.remove(startIdx, endIdx - startIdx + 1); } if (changeStart < changeEnd) { version++; } } if (changeStart < changeEnd) { fireHighlightsChange(changeStart, changeEnd); } } /** * Gets highlights from an area of a document. The HighlightsSequence is * computed using all the highlights present in this bag between the * startOffset and endOffset. * * @param startOffset The beginning of the area. * @param endOffset The end of the area. * * @return The HighlightsSequence which iterates through the * highlights in the given area of this bag. */ public HighlightsSequence getHighlights(int startOffset, int endOffset) { if (LOG.isLoggable(Level.FINE) && !(startOffset < endOffset)) { LOG.fine("startOffset must be less than endOffset: startOffset = " + //NOI18N startOffset + " endOffset = " + endOffset); //NOI18N } synchronized (marks) { return new Seq(version, startOffset, endOffset); } } /** * Removes all highlights previously added to this bag. */ public void clear() { int [] clearedArea; synchronized (marks) { clearedArea = clearImpl(); if (clearedArea != null) { version++; } } if (clearedArea != null) { fireHighlightsChange(clearedArea[0], clearedArea[1]); } } // ---------------------------------------------------------------------- // Package private API // ---------------------------------------------------------------------- /* package */ GapList getMarks() { return marks; } /* package */ GapList getAttributes() { return attributes; } // ---------------------------------------------------------------------- // Private implementation // ---------------------------------------------------------------------- private int [] addHighlightImpl(Position startPosition, Position endPosition, AttributeSet attributes) { if (startPosition.getOffset() == endPosition.getOffset()) { return null; } else { assert startPosition.getOffset() < endPosition.getOffset() : "Start position must be before the end position."; //NOI18N assert attributes != null : "Highlight attributes must not be null."; //NOI18N } if (mergeHighlights) { merge(startPosition, endPosition, attributes); } else { trim(startPosition, endPosition, attributes); } return new int [] { startPosition.getOffset(), endPosition.getOffset() }; } private void merge(Position startPosition, Position endPosition, AttributeSet attrSet) { AttributeSet lastKnownAttributes = null; int startIdx = indexBeforeOffset(startPosition.getOffset()); if (startIdx < 0) { startIdx = 0; marks.add(startIdx, startPosition); attributes.add(startIdx, attrSet); } else { Position mark = marks.get(startIdx); AttributeSet newAttribs = AttributesUtilities.createComposite(attrSet, attributes.get(startIdx)); lastKnownAttributes = attributes.get(startIdx); if (mark.getOffset() == startPosition.getOffset()) { attributes.set(startIdx, newAttribs); } else { startIdx++; marks.add(startIdx, startPosition); attributes.add(startIdx, newAttribs); } } for(int idx = startIdx + 1; ; idx++) { if (idx < marks.size()) { Position mark = marks.get(idx); if (mark.getOffset() < endPosition.getOffset()) { lastKnownAttributes = attributes.get(idx); attributes.set(idx, AttributesUtilities.createComposite(attrSet, lastKnownAttributes)); } else { if (mark.getOffset() > endPosition.getOffset()) { marks.add(idx, endPosition); attributes.add(idx, lastKnownAttributes); } break; } } else { marks.add(idx, endPosition); attributes.add(idx, lastKnownAttributes); break; } } } private void trim(Position startPosition, Position endPosition, AttributeSet attrSet) { int startIdx = indexBeforeOffset(startPosition.getOffset()); int endIdx = indexBeforeOffset(endPosition.getOffset(), startIdx < 0 ? 0 : startIdx, marks.size() - 1); // System.out.println("trim(" + startOffset + ", " + endOffset + ") : startIdx = " + startIdx + ", endIdx = " + endIdx); if (startIdx == endIdx) { AttributeSet original = null; if (startIdx != -1 && attributes.get(startIdx) != null) { original = attributes.get(startIdx); } if (startIdx != -1 && marks.get(startIdx).getOffset() == startPosition.getOffset()) { attributes.set(startIdx, attrSet); } else { startIdx++; marks.add(startIdx, startPosition); attributes.add(startIdx, attrSet); } startIdx++; marks.add(startIdx, endPosition); attributes.add(startIdx, original); } else { assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx; marks.set(endIdx, endPosition); attributes.set(endIdx, attributes.get(endIdx)); endIdx--; startIdx++; if (startIdx <= endIdx) { marks.set(startIdx, startPosition); attributes.set(startIdx, attrSet); } else { marks.add(startIdx, startPosition); attributes.add(startIdx, attrSet); } startIdx++; if (startIdx <= endIdx) { marks.remove(startIdx, endIdx - startIdx + 1); attributes.remove(startIdx, endIdx - startIdx + 1); } } } private int [] addAllHighlightsImpl(PositionsBag bag) { int changeStart = Integer.MAX_VALUE; int changeEnd = Integer.MIN_VALUE; GapList newMarks = bag.getMarks(); GapList newAttributes = bag.getAttributes(); for (int i = 0 ; i + 1 < newMarks.size(); i++) { Position mark1 = newMarks.get(i); Position mark2 = newMarks.get(i + 1); AttributeSet attrSet = newAttributes.get(i); if (attrSet == null) { // skip empty highlight continue; } addHighlightImpl(mark1, mark2, attrSet); if (changeStart == Integer.MAX_VALUE) { changeStart = mark1.getOffset(); } changeEnd = mark2.getOffset(); } if (changeStart != Integer.MAX_VALUE && changeEnd != Integer.MIN_VALUE) { return new int [] { changeStart, changeEnd }; } else { return null; } } private int [] clearImpl() { if (!marks.isEmpty()) { int changeStart = marks.get(0).getOffset(); int changeEnd = marks.get(marks.size() - 1).getOffset(); marks.clear(); attributes.clear(); return new int [] { changeStart, changeEnd }; } else { return null; } } private int indexBeforeOffset(int offset, int low, int high) { int idx = findElementIndex(offset, low, high); if (idx < 0) { idx = -idx - 1; // the insertion point: <0, size()> return idx - 1; } else { return idx; } } private int indexBeforeOffset(int offset) { return indexBeforeOffset(offset, 0, marks.size() - 1); } private int findElementIndex(int offset, int lowIdx, int highIdx) { if (lowIdx < 0 || highIdx > marks.size() - 1) { throw new IndexOutOfBoundsException("lowIdx = " + lowIdx + ", highIdx = " + highIdx + ", size = " + marks.size()); } int low = lowIdx; int high = highIdx; while (low <= high) { int index = (low + high) >> 1; // mid in the binary search int elemOffset = marks.get(index).getOffset(); if (elemOffset < offset) { low = index + 1; } else if (elemOffset > offset) { high = index - 1; } else { // exact offset found at index while (index > 0) { index--; if (marks.get(index).getOffset() < offset) { index++; break; } } return index; } } return -(low + 1); } private final class Seq implements HighlightsSequence { private long version; private int startOffset; private int endOffset; private int idx = -1; public Seq(long version, int startOffset, int endOffset) { this.version = version; this.startOffset = startOffset; this.endOffset = endOffset; } public boolean moveNext() { synchronized (PositionsBag.this.marks) { checkVersion(); if (idx == -1) { idx = indexBeforeOffset(startOffset); if (idx == -1 && marks.size() > 0) { idx = 0; } } else { idx++; } while (isIndexValid(idx)) { if (attributes.get(idx) != null) { return true; } // Skip any empty areas idx++; } return false; } } public int getStartOffset() { synchronized (PositionsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return Math.max(marks.get(idx).getOffset(), startOffset); } else { throw new NoSuchElementException(); } } } public int getEndOffset() { synchronized (PositionsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return Math.min(marks.get(idx + 1).getOffset(), endOffset); } else { throw new NoSuchElementException(); } } } public AttributeSet getAttributes() { synchronized (PositionsBag.this.marks) { assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N checkVersion(); if (isIndexValid(idx)) { return attributes.get(idx); } else { throw new NoSuchElementException(); } } } private boolean isIndexValid(int idx) { return idx >= 0 && idx + 1 < marks.size() && marks.get(idx).getOffset() < endOffset && marks.get(idx + 1).getOffset() > startOffset; } private void checkVersion() { if (PositionsBag.this.version != version) { throw new ConcurrentModificationException(); } } } // End of Seq class } Directory: /editor/lib2/test/ ============================= File [added]: .cvsignore Url: http://editor.netbeans.org/source/browse/editor/lib2/test/.cvsignore?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 3 -------------- lib results work File [added]: build.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/test/build.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 38 --------------- File [added]: cfg-unit.xml Url: http://editor.netbeans.org/source/browse/editor/lib2/test/cfg-unit.xml?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 81 --------------- Directory: /editor/lib2/test/unit/src/META-INF/services/ ======================================================== File [added]: org.netbeans.spi.editor.mimelookup.MimeDataProvider Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/META-INF/services/org.netbeans.spi.editor.mimelookup.MimeDataProvider?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- org.netbeans.modules.editor.lib2.highlighting.MemoryMimeDataProvider Directory: /editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/ ==================================================================================== File [added]: CompoundHighlightsContainerTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/CompoundHighlightsContainerTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 462 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ /* * ProxyHighlightLayerTest.java * JUnit based test * * Created on June 28, 2006, 5:44 PM */ package org.netbeans.modules.editor.lib2.highlighting; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.Random; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.support.PositionsBag; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.junit.NbTestCase; /** * * @author vita */ public class CompoundHighlightsContainerTest extends NbTestCase { public CompoundHighlightsContainerTest(String testName) { super(testName); } public void testSimple() { PlainDocument doc = new PlainDocument(); HighlightsContainer layer = createRandomBag(doc, "layer"); HighlightsSequence highlights = layer.getHighlights(0, 100); CompoundHighlightsContainer proxyLayer = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { layer }); HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); for ( ; highlights.moveNext(); ) { // Ignore empty highlights if (highlights.getStartOffset() == highlights.getEndOffset()) { continue; } assertTrue("Wrong number of proxy highlights", proxyHighlights.moveNext()); assertEquals("Start offset does not match", highlights.getStartOffset(), proxyHighlights.getStartOffset()); assertEquals("End offset does not match", highlights.getEndOffset(), proxyHighlights.getEndOffset()); assertTrue("Attributes do not match", highlights.getAttributes().isEqual(proxyHighlights.getAttributes())); } } public void testOrdering() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("attribute", "value-A"); attribsB.addAttribute("attribute", "value-B"); hsA.addHighlight(new SimplePosition(5), new SimplePosition(15), attribsA); hsB.addHighlight(new SimplePosition(10), new SimplePosition(20), attribsB); CompoundHighlightsContainer chc = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { hsA, hsB }); HighlightsSequence highlights = chc.getHighlights(0, Integer.MAX_VALUE); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("1. highlight - wrong attribs", "value-A", highlights.getAttributes().getAttribute("attribute")); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("2. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("3. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); } public void testConcurrentModification() throws Exception { checkConcurrentModificationOnMethod("moveNext"); checkConcurrentModificationOnMethod("getStartOffset"); checkConcurrentModificationOnMethod("getEndOffset"); checkConcurrentModificationOnMethod("getAttributes"); } private void checkConcurrentModificationOnMethod(String methodName) throws Exception { PlainDocument doc = new PlainDocument(); PositionsBag bag = createRandomBag(doc, "layer"); HighlightsContainer [] layers = new HighlightsContainer [] { bag }; { CompoundHighlightsContainer hb = new CompoundHighlightsContainer(doc, layers); HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); // Change the layers hb.setLayers(doc, layers); Throwable exc = null; try { Method m = hs.getClass().getMethod(methodName); m.invoke(hs); } catch (InvocationTargetException e) { exc = e.getCause(); } assertTrue("ConcurrentModificationException has not been thrown from " + methodName + "() after setLayers", exc instanceof ConcurrentModificationException); } { CompoundHighlightsContainer hb = new CompoundHighlightsContainer(doc, layers); HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); // Modify the bag bag.addHighlight(new SimplePosition(20), new SimplePosition(30), SimpleAttributeSet.EMPTY); Throwable exc = null; try { Method m = hs.getClass().getMethod(methodName); m.invoke(hs); } catch (InvocationTargetException e) { exc = e.getCause(); } assertTrue("ConcurrentModificationException has not been thrown from " + methodName + "() after changing the original bag", exc instanceof ConcurrentModificationException); } } public void testRandomMerging() { String [] layerNames = new String [] { "layer-1", "layer-2", "layer-3", }; PlainDocument doc = new PlainDocument(); HighlightsContainer [] layers = new HighlightsContainer [layerNames.length]; for(int i = 0; i < layers.length; i++) { layers[i] = createRandomBag(doc, layerNames[i]); }; CompoundHighlightsContainer proxyLayer = new CompoundHighlightsContainer(doc, layers); for (int pointer = 0; pointer <= 100; pointer++) { // Check the highlights String failMsg = null; Highlight [] highestPair = new Highlight [] { null, null }; Highlight [] proxyPair = new Highlight [] { null, null }; try { highestPair = new Highlight [] { null, null }; proxyPair = new Highlight [] { null, null }; // Find all highlights at the position ArrayList leftHighlights = new ArrayList(); ArrayList rightHighlights = new ArrayList(); for (int i = 0; i < layers.length; i++) { Highlight [] layerPair = findPair(pointer, layers[i].getHighlights(0, 100)); if (layerPair[0] != null) { leftHighlights.add(layerPair[0].getAttributes()); } if (layerPair[1] != null) { rightHighlights.add(layerPair[1].getAttributes()); } } if (!leftHighlights.isEmpty()) { highestPair[0] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( leftHighlights.toArray(new AttributeSet[leftHighlights.size()]))); } if (!rightHighlights.isEmpty()) { highestPair[1] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( rightHighlights.toArray(new AttributeSet[rightHighlights.size()]))); } // Find the proxy layer highlight at the position proxyPair = findPair(pointer, proxyLayer.getHighlights(0, 100)); for (int i = 0; i < 2; i++) { if (highestPair[i] != null && proxyPair[i] != null) { // Both highlights exist -> check they are the same if (!highestPair[i].getAttributes().isEqual(proxyPair[i].getAttributes())) { failMsg = (i == 0 ? "Left" : "Right") + "pair attributes do not match"; } } else if (highestPair[i] != null || proxyPair[i] != null) { // Both highlights should be null otherwise they would not match failMsg = (i == 0 ? "Left" : "Right") + " highlight doesn't match"; } } } catch (Throwable e) { failMsg = e.getMessage(); } if (failMsg != null) { // Dump the layers System.out.println("Dumping layers:"); for (int i = 0; i < layers.length; i++) { System.out.println(" layer[" + i + "] = " + layerNames[i] + "{"); for (HighlightsSequence highlights = layers[i].getHighlights(0, 100); highlights.moveNext(); ) { Highlight h = copyCurrentHighlight(highlights); System.out.println(" " + dumpHighlight(h)); } System.out.println(" } End of layer[" + i + "] -----------------"); } System.out.println("Dumping proxy layer: {"); for (HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); proxyHighlights.moveNext(); ) { Highlight h = copyCurrentHighlight(proxyHighlights); System.out.println(" " + dumpHighlight(h)); } System.out.println("} End of proxy layer -----------------------"); // Dump the pair that failed System.out.println("highest pair (pos = " + pointer + ") : " + dumpHighlight(highestPair[0]) + ", " + dumpHighlight(highestPair[1])); System.out.println(" proxy pair (pos = " + pointer + ") : " + dumpHighlight(proxyPair[0]) + ", " + dumpHighlight(proxyPair[1])); fail(failMsg + " (position = " + pointer + ")"); } } } public void testEvents() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); CompoundHighlightsContainer chc = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { hsA, hsB }); Listener listener = new Listener(); chc.addHighlightsChangeListener(listener); hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet()); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 10, listener.lastEventStartOffset); assertEquals("Wrong change end offset", 20, listener.lastEventEndOffset); listener.reset(); hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet()); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 11, listener.lastEventStartOffset); assertEquals("Wrong change end offset", 12, listener.lastEventEndOffset); } public void testEvents2() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet()); hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet()); CompoundHighlightsContainer chc = new CompoundHighlightsContainer(); Listener listener = new Listener(); chc.addHighlightsChangeListener(listener); // changing delegate layers fires event covering 'all' offsets chc.setLayers(doc, new HighlightsContainer [] { hsA, hsB }); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 0, listener.lastEventStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEventEndOffset); } private Highlight [] findPair(int offset, HighlightsSequence highlights) { Highlight left = null; Highlight right = null; for ( ; highlights.moveNext(); ) { if (highlights.getStartOffset() == highlights.getEndOffset()) { // ignore empty offsets continue; } if (offset > highlights.getStartOffset() && offset < highlights.getEndOffset()) { left = right = copyCurrentHighlight(highlights); } else if (offset == highlights.getEndOffset()) { left = copyCurrentHighlight(highlights); } else if (offset == highlights.getStartOffset()) { right = copyCurrentHighlight(highlights); } } return new Highlight [] { left, right }; } private Highlight copyCurrentHighlight(HighlightsSequence iterator) { return new Highlight( iterator.getStartOffset(), iterator.getEndOffset(), iterator.getAttributes() ); } private String dumpHighlight(Highlight h) { if (h == null) { return "< , , >"; } else { StringBuilder sb = new StringBuilder(); sb.append("<"); sb.append(h.getStartOffset()); sb.append(","); sb.append(h.getEndOffset()); sb.append(","); Enumeration en = h.getAttributes().getAttributeNames(); while (en.hasMoreElements()) { Object attrName = en.nextElement(); Object attrValue = h.getAttributes().getAttribute(attrName); sb.append("'"); sb.append(attrName.toString()); sb.append("' = '"); sb.append(attrValue == null ? "null" : attrValue.toString()); sb.append("'"); if (en.hasMoreElements()) { sb.append(", "); } } sb.append(">"); return sb.toString(); } } private PositionsBag createRandomBag(Document doc, String bagId) { PositionsBag bag = new PositionsBag(doc, false); Random rand = new Random(System.currentTimeMillis()); int attrIdx = 0; int startOffset = 0; int endOffset = 100; int maxGapSize = Math.max((int) (endOffset - startOffset) / 10, 1); int maxHighlightSize = Math.max((int) (endOffset - startOffset) / 2, 1); for (int pointer = startOffset + rand.nextInt(maxGapSize); pointer <= endOffset; ) { int highlightSize = rand.nextInt(maxHighlightSize); SimpleAttributeSet attributes = new SimpleAttributeSet(); attributes.addAttribute("AttrName-" + bagId + "-" + attrIdx, "AttrValue"); attrIdx++; if (pointer + highlightSize < endOffset) { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(pointer + highlightSize), attributes); } else { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(endOffset), attributes); } // move the pointer pointer += highlightSize + rand.nextInt(maxGapSize); } return bag; } private static final class Highlight { private int startOffset; private int endOffset; private AttributeSet attributes; public Highlight(int startOffset, int endOffset, AttributeSet attributes) { this.startOffset = startOffset; this.endOffset = endOffset; this.attributes = attributes; } public int getStartOffset() { return startOffset; } public void setStartOffset(int startOffset) { this.startOffset = startOffset; } public int getEndOffset() { return endOffset; } public void setEndOffset(int endOffset) { this.endOffset = endOffset; } public AttributeSet getAttributes() { return attributes; } public void setAttributes(AttributeSet attributes) { this.attributes = attributes; } } // End of H class private static final class Listener implements HighlightsChangeListener { public int eventsCnt = 0; public int lastEventStartOffset = 0; public int lastEventEndOffset = 0; public void highlightChanged(HighlightsChangeEvent event) { eventsCnt++; lastEventStartOffset = event.getStartOffset(); lastEventEndOffset = event.getEndOffset(); } public void reset() { eventsCnt = 0; lastEventStartOffset = 0; lastEventEndOffset = 0; } } // End of Listener class private static final class SimplePosition implements Position { private int offset; public SimplePosition(int offset) { this.offset = offset; } public int getOffset() { return offset; } } // End of SimplePosition class private void dumpHighlights(HighlightsSequence seq) { System.out.println("Dumping highlights from: " + seq + "{"); while(seq.moveNext()) { System.out.println("<" + seq.getStartOffset() + ", " + seq.getEndOffset() + ", " + seq.getAttributes() + ">"); } System.out.println("} --- End of Dumping highlights from: " + seq + " ---------------------"); } } File [added]: HighlightingManagerTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingManagerTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 789 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Random; import javax.swing.JEditorPane; import javax.swing.text.AttributeSet; import javax.swing.text.DefaultEditorKit; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.spi.editor.highlighting.ZOrder; import org.openide.util.Lookup; /** * * @author vita */ public class HighlightingManagerTest extends NbTestCase { /** Creates a new instance of HighlightingManagerTest */ public HighlightingManagerTest(String name) { super(name); } public void testSimple() { HighlightingManager hm = HighlightingManager.getInstance(); assertNotNull("Can't get instance of HighlightingManager", hm); JEditorPane pane = new JEditorPane(); HighlightsContainer hc = hm.getHighlights(pane, HighlightsLayerFilter.IDENTITY); assertNotNull("Can't get fixed HighlightsContainer", hc); assertFalse("There should be no fixed highlights", hc.getHighlights(0, Integer.MAX_VALUE).moveNext()); } public void testSimpleLayer() { OffsetsBag bag = new OffsetsBag(new PlainDocument()); MemoryMimeDataProvider.reset(null); MemoryMimeDataProvider.addInstances( "text/plain", new SingletonLayerFactory("layer", ZOrder.DEFAULT_RACK, true, bag)); JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane.getContentType()); HighlightingManager hm = HighlightingManager.getInstance(); HighlightsContainer hc = hm.getHighlights(pane, HighlightsLayerFilter.IDENTITY); assertNotNull("Can't get fixed HighlightsContainer", hc); assertFalse("There should be no fixed highlights", hc.getHighlights(0, Integer.MAX_VALUE).moveNext()); SimpleAttributeSet attributes = new SimpleAttributeSet(); attributes.addAttribute("attrib-A", "value"); bag.addHighlight(10, 20, attributes); HighlightsSequence highlights = hc.getHighlights(0, Integer.MAX_VALUE); assertTrue("Highlight has not been added", highlights.moveNext()); assertEquals("Wrong start offset", 10, highlights.getStartOffset()); assertEquals("Wrong end offset", 20, highlights.getEndOffset()); assertEquals("Can't find attribute", "value", highlights.getAttributes().getAttribute("attrib-A")); } // test multiple layers, merging, ordering public void testMultipleLayers() { OffsetsBag bagA = new OffsetsBag(new PlainDocument()); OffsetsBag bagB = new OffsetsBag(new PlainDocument()); OffsetsBag bagC = new OffsetsBag(new PlainDocument()); OffsetsBag bagD = new OffsetsBag(new PlainDocument()); MemoryMimeDataProvider.reset(null); MemoryMimeDataProvider.addInstances("text/plain", new SingletonLayerFactory("layerB", ZOrder.above("layerA"), false, bagB), new SingletonLayerFactory("layerD", ZOrder.above("layerC"), true, bagD), new SingletonLayerFactory("layerA", ZOrder.DEFAULT_RACK, true, bagA), new SingletonLayerFactory("layerC", ZOrder.above("layerB"), true, bagC) ); JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane.getContentType()); HighlightingManager hm = HighlightingManager.getInstance(); HighlightsContainer variableHC = hm.getHighlights(pane, VARIABLE_SIZE_LAYERS); assertNotNull("Can't get variable HighlightsContainer", variableHC); assertFalse("There should be no variable highlights", variableHC.getHighlights(0, Integer.MAX_VALUE).moveNext()); HighlightsContainer fixedHC = hm.getHighlights(pane, FIXED_SIZE_LAYERS); assertNotNull("Can't get fixed HighlightsContainer", fixedHC); assertFalse("There should be no fixed highlights", fixedHC.getHighlights(0, Integer.MAX_VALUE).moveNext()); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); SimpleAttributeSet attribsD = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "value"); attribsA.addAttribute("commonAttribute", "set-A-value"); attribsB.addAttribute("set-B", "value"); attribsB.addAttribute("commonAttribute", "set-B-value"); attribsC.addAttribute("set-C", "value"); attribsC.addAttribute("commonAttribute", "set-C-value"); attribsD.addAttribute("set-D", "value"); attribsD.addAttribute("commonAttribute", "set-D-value"); bagA.addHighlight(10, 20, attribsA); bagB.addHighlight(15, 25, attribsB); bagC.addHighlight(10, 20, attribsC); bagD.addHighlight(15, 25, attribsD); // Check fixed-size leyers sequence - should be C, D HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 10, fixed.getStartOffset()); assertEquals("Wrong end offset", 15, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-D"); assertEquals("Wrong commonAttribute value", "set-C-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 15, fixed.getStartOffset()); assertEquals("Wrong end offset", 20, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 20, fixed.getStartOffset()); assertEquals("Wrong end offset", 25, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-C"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check variable-size leyers sequence - should be A, B HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 10, variable.getStartOffset()); assertEquals("Wrong end offset", 15, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-C", "set-D", "set-B"); assertEquals("Wrong commonAttribute value", "set-A-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 15, variable.getStartOffset()); assertEquals("Wrong end offset", 20, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 20, variable.getStartOffset()); assertEquals("Wrong end offset", 25, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-C", "set-D", "set-A"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); } // test events fired from HCs when changing highlights on a layer public void testChangesInLayerFireEvents() { OffsetsBag bagA = new OffsetsBag(new PlainDocument()); OffsetsBag bagB = new OffsetsBag(new PlainDocument()); OffsetsBag bagC = new OffsetsBag(new PlainDocument()); OffsetsBag bagD = new OffsetsBag(new PlainDocument()); MemoryMimeDataProvider.reset(null); MemoryMimeDataProvider.addInstances("text/plain", new SingletonLayerFactory("layerB", ZOrder.above("layerA"), false, bagB), new SingletonLayerFactory("layerD", ZOrder.above("layerC"), true, bagD), new SingletonLayerFactory("layerA", ZOrder.DEFAULT_RACK, true, bagA), new SingletonLayerFactory("layerC", ZOrder.above("layerB"), true, bagC) ); JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane.getContentType()); HighlightingManager hm = HighlightingManager.getInstance(); // Test the variable-size layers - A,B Listener variableL = new Listener(); HighlightsContainer variableHC = hm.getHighlights(pane, VARIABLE_SIZE_LAYERS); assertNotNull("Can't get variable HighlightsContainer", variableHC); assertFalse("There should be no variable highlights", variableHC.getHighlights(0, Integer.MAX_VALUE).moveNext()); variableHC.addHighlightsChangeListener(variableL); bagA.addHighlight(10, 20, SimpleAttributeSet.EMPTY); assertEquals("Wrong number of events", 1, variableL.eventsCnt); assertEquals("Wrong change start offset", 10, variableL.lastStartOffset); assertEquals("Wrong change end offset", 20, variableL.lastEndOffset); variableL.reset(); bagB.addHighlight(5, 15, SimpleAttributeSet.EMPTY); assertEquals("Wrong number of events", 1, variableL.eventsCnt); assertEquals("Wrong change start offset", 5, variableL.lastStartOffset); assertEquals("Wrong change end offset", 15, variableL.lastEndOffset); // Test the fixed-size layers Listener fixedL = new Listener(); HighlightsContainer fixedHC = hm.getHighlights(pane, FIXED_SIZE_LAYERS); assertNotNull("Can't get fixed HighlightsContainer", fixedHC); assertFalse("There should be no fixed highlights", fixedHC.getHighlights(0, Integer.MAX_VALUE).moveNext()); fixedHC.addHighlightsChangeListener(fixedL); bagC.addHighlight(20, 50, SimpleAttributeSet.EMPTY); assertEquals("Wrong number of events", 1, fixedL.eventsCnt); assertEquals("Wrong change start offset", 20, fixedL.lastStartOffset); assertEquals("Wrong change end offset", 50, fixedL.lastEndOffset); fixedL.reset(); bagD.addHighlight(0, 30, SimpleAttributeSet.EMPTY); assertEquals("Wrong number of events", 1, fixedL.eventsCnt); assertEquals("Wrong change start offset", 0, fixedL.lastStartOffset); assertEquals("Wrong change end offset", 30, fixedL.lastEndOffset); } // test adding/removing a layer public void testAddingRemovingLayers() { final String mimeType = "text/plain"; OffsetsBag bagA = new OffsetsBag(new PlainDocument()); OffsetsBag bagB = new OffsetsBag(new PlainDocument()); OffsetsBag bagC = new OffsetsBag(new PlainDocument()); OffsetsBag bagD = new OffsetsBag(new PlainDocument()); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); SimpleAttributeSet attribsD = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "value"); attribsA.addAttribute("commonAttribute", "set-A-value"); attribsB.addAttribute("set-B", "value"); attribsB.addAttribute("commonAttribute", "set-B-value"); attribsC.addAttribute("set-C", "value"); attribsC.addAttribute("commonAttribute", "set-C-value"); attribsD.addAttribute("set-D", "value"); attribsD.addAttribute("commonAttribute", "set-D-value"); bagA.addHighlight(10, 20, attribsA); bagB.addHighlight(15, 25, attribsB); bagC.addHighlight(50, 60, attribsC); bagD.addHighlight(55, 65, attribsD); SingletonLayerFactory layerA = new SingletonLayerFactory("layerA", ZOrder.DEFAULT_RACK, true, bagA); SingletonLayerFactory layerB = new SingletonLayerFactory("layerB", ZOrder.DEFAULT_RACK.aboveLayers("layerA"), false, bagB); SingletonLayerFactory layerC = new SingletonLayerFactory("layerC", ZOrder.DEFAULT_RACK.aboveLayers("layerA", "layerB"), true, bagC); SingletonLayerFactory layerD = new SingletonLayerFactory("layerD", ZOrder.DEFAULT_RACK.aboveLayers("layerA", "layerB", "layerC"), true, bagD); MemoryMimeDataProvider.reset(null); MemoryMimeDataProvider.addInstances(mimeType, layerA, layerD); JEditorPane pane = new JEditorPane(); pane.setEditorKit(new SimpleKit(mimeType)); assertEquals("The pane has got wrong mime type", mimeType, pane.getContentType()); final HighlightingManager hm = HighlightingManager.getInstance(); final HighlightsContainer variableHC = hm.getHighlights(pane, VARIABLE_SIZE_LAYERS); final HighlightsContainer fixedHC = hm.getHighlights(pane, FIXED_SIZE_LAYERS); assertNotNull("Can't get variable HighlightsContainer", variableHC); assertNotNull("Can't get fixed HighlightsContainer", fixedHC); { HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no variable highlights", variable.moveNext()); } { HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 10, fixed.getStartOffset()); assertEquals("Wrong end offset", 20, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-A", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-B", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-A-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 55, fixed.getStartOffset()); assertEquals("Wrong end offset", 65, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-C"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); } // Add layer B - that should put A, B in variableHC and leave D in fixedHC MemoryMimeDataProvider.addInstances(mimeType, layerB); { HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 10, variable.getStartOffset()); assertEquals("Wrong end offset", 15, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-B", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-A-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 15, variable.getStartOffset()); assertEquals("Wrong end offset", 20, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 20, variable.getStartOffset()); assertEquals("Wrong end offset", 25, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-A", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); } { HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 55, fixed.getStartOffset()); assertEquals("Wrong end offset", 65, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-C"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); } // Add layer C - that should leave A, B in variableHC, should also leave D in fixedHC and add C in fixedHC MemoryMimeDataProvider.addInstances(mimeType, layerC); { HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 10, variable.getStartOffset()); assertEquals("Wrong end offset", 15, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-B", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-A-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 15, variable.getStartOffset()); assertEquals("Wrong end offset", 20, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-A", "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", variable.moveNext()); assertEquals("Wrong start offset", 20, variable.getStartOffset()); assertEquals("Wrong end offset", 25, variable.getEndOffset()); assertAttribContains("Can't find attribute", variable.getAttributes(), "set-B", "commonAttribute"); assertAttribNotContains("The attribute should not be there", variable.getAttributes(), "set-A", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-B-value", variable.getAttributes().getAttribute("commonAttribute")); } { HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 50, fixed.getStartOffset()); assertEquals("Wrong end offset", 55, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-D"); assertEquals("Wrong commonAttribute value", "set-C-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 55, fixed.getStartOffset()); assertEquals("Wrong end offset", 60, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 60, fixed.getStartOffset()); assertEquals("Wrong end offset", 65, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-C"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); } // Remove layer B - that should put A in fixedHC, should also leave C, D in fixedHC MemoryMimeDataProvider.removeInstances(mimeType, layerB); { HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no variable highlights", variable.moveNext()); } { HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); // Check 1. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 10, fixed.getStartOffset()); assertEquals("Wrong end offset", 20, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-A", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-B", "set-C", "set-D"); assertEquals("Wrong commonAttribute value", "set-A-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 2. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 50, fixed.getStartOffset()); assertEquals("Wrong end offset", 55, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-D"); assertEquals("Wrong commonAttribute value", "set-C-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 3. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 55, fixed.getStartOffset()); assertEquals("Wrong end offset", 60, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-C", "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); // Check 4. highlight assertTrue("Wrong number of highlights", fixed.moveNext()); assertEquals("Wrong start offset", 60, fixed.getStartOffset()); assertEquals("Wrong end offset", 65, fixed.getEndOffset()); assertAttribContains("Can't find attribute", fixed.getAttributes(), "set-D", "commonAttribute"); assertAttribNotContains("The attribute should not be there", fixed.getAttributes(), "set-A", "set-B", "set-C"); assertEquals("Wrong commonAttribute value", "set-D-value", fixed.getAttributes().getAttribute("commonAttribute")); } // Remove all remaining layers - that should remove all highlighs MemoryMimeDataProvider.removeInstances(mimeType, layerA, layerC, layerD); { HighlightsSequence variable = variableHC.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no variable highlights", variable.moveNext()); } { HighlightsSequence fixed = fixedHC.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no fixed highlights", fixed.moveNext()); } } // test events fired from HCs when adding/removing a layer public void testEventsWhenAddingRemovingLayers() { OffsetsBag bagA = new OffsetsBag(new PlainDocument()); OffsetsBag bagB = new OffsetsBag(new PlainDocument()); SingletonLayerFactory layerA = new SingletonLayerFactory("layerA", ZOrder.DEFAULT_RACK, true, bagA); SingletonLayerFactory layerB = new SingletonLayerFactory("layerB", ZOrder.DEFAULT_RACK, false, bagB); MemoryMimeDataProvider.reset(null); JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane.getContentType()); final HighlightingManager hm = HighlightingManager.getInstance(); final HighlightsContainer hc = hm.getHighlights(pane, HighlightsLayerFilter.IDENTITY); assertNotNull("Can't get fixed HighlightsContainer", hc); // There should be no layers and no highlights { HighlightsSequence fixed = hc.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no highlights", fixed.moveNext()); } Listener listener = new Listener(); hc.addHighlightsChangeListener(listener); // Add layer A - it's a fixed-size layer listener.reset(); MemoryMimeDataProvider.addInstances("text/plain", layerA); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertNull("Wrong change start position", listener.lastStartPosition); assertNull("Wrong change end position", listener.lastEndPosition); assertEquals("Wrong change start offset", 0, listener.lastStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEndOffset); { HighlightsSequence fixed = hc.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no highlights", fixed.moveNext()); } // Add layer B - it's a variable-size layer listener.reset(); MemoryMimeDataProvider.addInstances("text/plain", layerB); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertNull("Wrong change start position", listener.lastStartPosition); assertNull("Wrong change end position", listener.lastEndPosition); assertEquals("Wrong change start offset", 0, listener.lastStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEndOffset); { HighlightsSequence fixed = hc.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no highlights", fixed.moveNext()); } // Remove layer A - it's a fixed-size layer listener.reset(); MemoryMimeDataProvider.removeInstances("text/plain", layerA); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertNull("Wrong change start position", listener.lastStartPosition); assertNull("Wrong change end position", listener.lastEndPosition); assertEquals("Wrong change start offset", 0, listener.lastStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEndOffset); { HighlightsSequence fixed = hc.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no highlights", fixed.moveNext()); } // Remove layer B - it's a variable-size layer listener.reset(); MemoryMimeDataProvider.removeInstances("text/plain", layerB); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertNull("Wrong change start position", listener.lastStartPosition); assertNull("Wrong change end position", listener.lastEndPosition); assertEquals("Wrong change start offset", 0, listener.lastStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEndOffset); { HighlightsSequence fixed = hc.getHighlights(0, Integer.MAX_VALUE); assertFalse("There should be no highlights", fixed.moveNext()); } } // test getting independent HCs for different JEditorPanes with the same mime type public void testCaching() { MemoryMimeDataProvider.reset(null); HighlightingManager hm = HighlightingManager.getInstance(); JEditorPane pane1 = new JEditorPane(); pane1.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane1.getContentType()); JEditorPane pane2 = new JEditorPane(); pane2.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane2.getContentType()); { HighlightsContainer hc1_A = hm.getHighlights(pane1, HighlightsLayerFilter.IDENTITY); HighlightsContainer hc1_B = hm.getHighlights(pane1, HighlightsLayerFilter.IDENTITY); assertSame("HighlightsContainer is not cached", hc1_A, hc1_B); HighlightsContainer hc2 = hm.getHighlights(pane2, HighlightsLayerFilter.IDENTITY); assertNotSame("HighlightsContainer should not be shared between JEPs", hc1_A, hc2); } gc(); { int hc1_A_hash = System.identityHashCode(hm.getHighlights(pane1, HighlightsLayerFilter.IDENTITY)); int hc1_B_hash = System.identityHashCode(hm.getHighlights(pane1, HighlightsLayerFilter.IDENTITY)); assertEquals("HighlightsContainer is not cached (different hash codes)", hc1_A_hash, hc1_B_hash); } } // test that bags and everything is GCed when the JEditorPane, which they were created for, is gone public void testCachedInstancesGCed() { MemoryMimeDataProvider.reset(null); HighlightingManager hm = HighlightingManager.getInstance(); gc(); assertEquals("The CACHE should be empty", 0, hm.CACHE.size()); JEditorPane pane = new JEditorPane(); pane.setContentType("text/plain"); assertEquals("The pane has got wrong mime type", "text/plain", pane.getContentType()); HighlightsContainer hc = hm.getHighlights(pane, HighlightsLayerFilter.IDENTITY); assertNotNull("Can't get HighlightsContainer", hc); assertEquals("Wrong number of highlights in the CACHE", 1, hm.CACHE.size()); WeakReference refPane = new WeakReference(pane); WeakReference refHc = new WeakReference(hc); // reset hard references pane = null; hc = null; assertGC("JEP has not been GCed", refPane); assertGC("HC has not been GCed", refHc); assertEquals("The CACHE should be empty", 0, hm.CACHE.size()); } private void assertAttribContains(String msg, AttributeSet as, String... keys) { // System.out.print("assertAttribContains: attributes: "); // for(Enumeration attribKeys = as.getAttributeNames(); attribKeys.hasMoreElements(); ) { // Object key = attribKeys.nextElement(); // Object value = as.getAttribute(key); // System.out.print("'" + key + "' = '" + value + "', "); // } // System.out.println(); assertEquals(msg, keys.length, as.getAttributeCount()); for (String key : keys) { if (null == as.getAttribute(key)) { fail(msg + " attribute key: " + key); } } } private void assertAttribNotContains(String msg, AttributeSet as, String... keys) { // System.out.print("assertAttribNotContains: attributes: "); // for(Enumeration attribKeys = as.getAttributeNames(); attribKeys.hasMoreElements(); ) { // Object key = attribKeys.nextElement(); // Object value = as.getAttribute(key); // System.out.print("'" + key + "' = '" + value + "', "); // } // System.out.println(); for (String key : keys) { if (null != as.getAttribute(key) || as.isDefined(key)) { fail(msg + " attribute key: " + key); } } } private void gc() { Random rand = new Random(System.currentTimeMillis()); for(int i = 0; i < 5; i++) { System.gc(); try { Thread.sleep(123 + rand.nextInt(1000)); } catch (InterruptedException e) { // ignore } } } private void dumpLookupContents(String mimePath) { Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimePath)); Lookup.Result result = lookup.lookupResult(Object.class); Collection> items = result.allItems(); System.out.println("Lookup for " + mimePath + " : {"); for(Lookup.Item item : items) { System.out.println(" " + item.getDisplayName()); } System.out.println("} end of Lookup for " + mimePath + " ----"); } private static final class SingletonLayerFactory implements HighlightsLayerFactory { private String id; private ZOrder zOrder; private boolean fixed; private HighlightsContainer container; public SingletonLayerFactory(String id, ZOrder zOrder, boolean fixed, HighlightsContainer hc) { this.id = id; this.zOrder = zOrder; this.fixed = fixed; this.container = hc; } public HighlightsLayer[] createLayers(HighlightsLayerFactory.Context context) { return new HighlightsLayer [] { HighlightsLayer.create(id, zOrder, fixed, container) }; } public String toString() { return super.toString() + "; id = " + id; } } // End of HLFactory private static final class SimplePosition implements Position { private int offset; public SimplePosition(int offset) { this.offset = offset; } public int getOffset() { return offset; } } // End of SimplePosition class private static final class SimpleKit extends DefaultEditorKit { private String mimeType; public SimpleKit(String mimeType) { this.mimeType = mimeType; } public String getContentType() { return mimeType; } } // End of SimpleKit class private static final class Listener implements HighlightsChangeListener { public int eventsCnt = 0; public int lastStartOffset; public int lastEndOffset; public Position lastStartPosition; public Position lastEndPosition; public void highlightChanged(HighlightsChangeEvent event) { eventsCnt++; lastStartOffset = event.getStartOffset(); lastEndOffset = event.getEndOffset(); } public void reset() { eventsCnt = 0; lastStartOffset = -1; lastEndOffset = -1; } } // End of Listener class private static final HighlightsLayerFilter FIXED_SIZE_LAYERS = new HighlightsLayerFilter() { public List filterLayers(List layers) { ArrayList filteredLayers = new ArrayList(); for(int i = layers.size() - 1; i >= 0; i--) { HighlightsLayer layer = layers.get(i); HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); if (!layerAccessor.isFixedSize()) { break; } filteredLayers.add(0, layer); } return filteredLayers; } }; private static final HighlightsLayerFilter VARIABLE_SIZE_LAYERS = new HighlightsLayerFilter() { public List filterLayers(List layers) { ArrayList filteredLayers = new ArrayList(); boolean fixedSize = true; for(int i = layers.size() - 1; i >= 0; i--) { HighlightsLayer layer = layers.get(i); HighlightsLayerAccessor layerAccessor = HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); if (!layerAccessor.isFixedSize()) { fixedSize = false; } if (!fixedSize) { filteredLayers.add(0, layer); } } return filteredLayers; } }; } File [added]: MemoryMimeDataProvider.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/MemoryMimeDataProvider.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 126 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.spi.editor.mimelookup.MimeDataProvider; import org.openide.util.Lookup; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; /** * * @author vita */ public final class MemoryMimeDataProvider implements MimeDataProvider { private static final HashMap CACHE = new HashMap(); /** Creates a new instance of MemoryMimeDataProvider */ public MemoryMimeDataProvider() { } public Lookup getLookup(MimePath mimePath) { return getLookup(mimePath.getPath(), true); } public static void addInstances(String mimePath, Object... instances) { assert mimePath != null : "Mime path can't be null"; getLookup(mimePath, true).addInstances(instances); } public static void removeInstances(String mimePath, Object... instances) { assert mimePath != null : "Mime path can't be null"; getLookup(mimePath, true).removeInstances(instances); } public static void reset(String mimePath) { if (mimePath == null) { synchronized (CACHE) { for(Lkp lookup : CACHE.values()) { lookup.reset(); } } } else { Lkp lookup = getLookup(mimePath, false); if (lookup != null) { lookup.reset(); } } } private static Lkp getLookup(String mimePath, boolean create) { synchronized (CACHE) { Lkp lookup = CACHE.get(mimePath); if (lookup == null && create) { lookup = new Lkp(); CACHE.put(mimePath, lookup); } return lookup; } } private static final class Lkp extends AbstractLookup { private ArrayList all = new ArrayList(); private InstanceContent contents; public Lkp() { this(new InstanceContent()); } private Lkp(InstanceContent ic) { super(ic); this.contents = ic; } public void addInstances(Object... instances) { all.addAll(Arrays.asList(instances)); contents.set(all, null); } public void removeInstances(Object... instances) { ArrayList newAll = new ArrayList(); loop: for(Object oo : all) { for(Object o : instances) { if (o == oo) { continue loop; } } newAll.add(oo); } all = newAll; contents.set(all, null); } public void reset() { all.clear(); contents.set(all, null); } } // End of Lkp class } File [added]: ProxyHighlightsContainerTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/ProxyHighlightsContainerTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 521 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ /* * ProxyHighlightLayerTest.java * JUnit based test * * Created on June 28, 2006, 5:44 PM */ package org.netbeans.modules.editor.lib2.highlighting; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.Random; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.spi.editor.highlighting.AttributesUtilities; import org.netbeans.spi.editor.highlighting.support.PositionsBag; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsSequence; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; /** * * @author vita */ public class ProxyHighlightsContainerTest extends NbTestCase { public ProxyHighlightsContainerTest(String testName) { super(testName); } public void testSequence2Marks() { OffsetsBag bag = new OffsetsBag(new PlainDocument()); SimpleAttributeSet attrsA = new SimpleAttributeSet(); SimpleAttributeSet attrsB = new SimpleAttributeSet(); SimpleAttributeSet attrsC = new SimpleAttributeSet(); attrsA.addAttribute("set-name", "attrsA"); attrsB.addAttribute("set-name", "attrsB"); attrsC.addAttribute("set-name", "attrsC"); bag.addHighlight(10, 20, attrsA); ProxyHighlightsContainer.Sequence2Marks s2m = new ProxyHighlightsContainer.Sequence2Marks(bag.getHighlights(0, 100), 0, 100); checkSequence2Marks(s2m, new Object [] { 10, attrsA, 20, null }); bag.addHighlight(30, 40, attrsB); s2m = new ProxyHighlightsContainer.Sequence2Marks(bag.getHighlights(0, 100), 0, 100); checkSequence2Marks(s2m, new Object [] { 10, attrsA, 20, null, 30, attrsB, 40, null }); bag.addHighlight(20, 30, attrsC); s2m = new ProxyHighlightsContainer.Sequence2Marks(bag.getHighlights(0, 100), 0, 100); checkSequence2Marks(s2m, new Object [] { 10, attrsA, 20, attrsC, 30, attrsB, 40, null }); s2m = new ProxyHighlightsContainer.Sequence2Marks(bag.getHighlights(0, 100), 15, 35); checkSequence2Marks(s2m, new Object [] { 15, attrsA, 20, attrsC, 30, attrsB, 35, null }); s2m = new ProxyHighlightsContainer.Sequence2Marks(bag.getHighlights(0, 100), 22, 28); checkSequence2Marks(s2m, new Object [] { 22, attrsC, 28, null }); } private void checkSequence2Marks(ProxyHighlightsContainer.Sequence2Marks s2m, Object... marks) { int cnt = 0; while (s2m.moveNext()) { assertTrue("Too many marks", cnt < marks.length); assertFalse("isFinished flag is not false", s2m.isFinished()); assertEquals("Wrong mark[" + cnt + "] offset", ((Integer) marks[2 * cnt]).intValue(), s2m.getMarkOffset()); assertSame("Wrong mark[" + cnt + "] Attributes", marks[2 * cnt + 1], s2m.getMarkAttributes()); if (cnt == 0) { assertEquals("Wrong previous mark offset", -1, s2m.getPreviousMarkOffset()); assertNull("Wrong previous mark Attributes", s2m.getPreviousMarkAttributes()); } else { assertEquals("Wrong previous mark offset", ((Integer) marks[2 * (cnt - 1)]).intValue(), s2m.getPreviousMarkOffset()); assertSame("Wrong previous mark Attributes", marks[2 * (cnt - 1) + 1], s2m.getPreviousMarkAttributes()); } cnt++; } assertEquals("Wrong number of marks", marks.length / 2, cnt); assertTrue("isFinished flag is not true", s2m.isFinished()); } public void testSimple() { PlainDocument doc = new PlainDocument(); HighlightsContainer layer = createRandomBag(doc, "layer"); HighlightsSequence highlights = layer.getHighlights(0, 100); ProxyHighlightsContainer proxyLayer = new ProxyHighlightsContainer(new HighlightsContainer [] { layer }); HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); for ( ; highlights.moveNext(); ) { // Ignore empty highlights if (highlights.getStartOffset() == highlights.getEndOffset()) { continue; } assertTrue("Wrong number of proxy highlights", proxyHighlights.moveNext()); assertEquals("Start offset does not match", highlights.getStartOffset(), proxyHighlights.getStartOffset()); assertEquals("End offset does not match", highlights.getEndOffset(), proxyHighlights.getEndOffset()); assertTrue("Attributes do not match", highlights.getAttributes().isEqual(proxyHighlights.getAttributes())); } } public void testOrdering() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("attribute", "value-A"); attribsB.addAttribute("attribute", "value-B"); hsA.addHighlight(new SimplePosition(5), new SimplePosition(15), attribsA); hsB.addHighlight(new SimplePosition(10), new SimplePosition(20), attribsB); ProxyHighlightsContainer chc = new ProxyHighlightsContainer(new HighlightsContainer [] { hsA, hsB }); HighlightsSequence highlights = chc.getHighlights(0, Integer.MAX_VALUE); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("1. highlight - wrong attribs", "value-A", highlights.getAttributes().getAttribute("attribute")); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("2. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); assertTrue("Wrong number of highlights", highlights.moveNext()); assertEquals("3. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); } public void testConcurrentModification() throws Exception { checkConcurrentModificationOnMethod("moveNext"); checkConcurrentModificationOnMethod("getStartOffset"); checkConcurrentModificationOnMethod("getEndOffset"); checkConcurrentModificationOnMethod("getAttributes"); } private void checkConcurrentModificationOnMethod(String methodName) throws Exception { PlainDocument doc = new PlainDocument(); PositionsBag bag = createRandomBag(doc, "layer"); HighlightsContainer [] layers = new HighlightsContainer [] { bag }; { ProxyHighlightsContainer hb = new ProxyHighlightsContainer(layers); HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); // Change the layers hb.setLayers(layers); Throwable exc = null; try { Method m = hs.getClass().getMethod(methodName); m.invoke(hs); } catch (InvocationTargetException e) { exc = e.getCause(); } assertTrue("ConcurrentModificationException has not been thrown from " + methodName + "() after setLayers", exc instanceof ConcurrentModificationException); } { ProxyHighlightsContainer hb = new ProxyHighlightsContainer(layers); HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); // Modify the bag bag.addHighlight(new SimplePosition(20), new SimplePosition(30), SimpleAttributeSet.EMPTY); Throwable exc = null; try { Method m = hs.getClass().getMethod(methodName); m.invoke(hs); } catch (InvocationTargetException e) { exc = e.getCause(); } assertTrue("ConcurrentModificationException has not been thrown from " + methodName + "() after changing the original bag", exc instanceof ConcurrentModificationException); } } public void testRandomMerging() { String [] layerNames = new String [] { "layer-1", "layer-2", "layer-3", }; PlainDocument doc = new PlainDocument(); HighlightsContainer [] layers = new HighlightsContainer [layerNames.length]; for(int i = 0; i < layers.length; i++) { layers[i] = createRandomBag(doc, layerNames[i]); }; ProxyHighlightsContainer proxyLayer = new ProxyHighlightsContainer(layers); for (int pointer = 0; pointer <= 100; pointer++) { // Check the highlights String failMsg = null; Highlight [] highestPair = new Highlight [] { null, null }; Highlight [] proxyPair = new Highlight [] { null, null }; try { highestPair = new Highlight [] { null, null }; proxyPair = new Highlight [] { null, null }; // Find all highlights at the position ArrayList leftHighlights = new ArrayList(); ArrayList rightHighlights = new ArrayList(); for (int i = 0; i < layers.length; i++) { Highlight [] layerPair = findPair(pointer, layers[i].getHighlights(0, 100)); if (layerPair[0] != null) { leftHighlights.add(layerPair[0].getAttributes()); } if (layerPair[1] != null) { rightHighlights.add(layerPair[1].getAttributes()); } } if (!leftHighlights.isEmpty()) { highestPair[0] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( leftHighlights.toArray(new AttributeSet[leftHighlights.size()]))); } if (!rightHighlights.isEmpty()) { highestPair[1] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( rightHighlights.toArray(new AttributeSet[rightHighlights.size()]))); } // Find the proxy layer highlight at the position proxyPair = findPair(pointer, proxyLayer.getHighlights(0, 100)); for (int i = 0; i < 2; i++) { if (highestPair[i] != null && proxyPair[i] != null) { // Both highlights exist -> check they are the same if (!highestPair[i].getAttributes().isEqual(proxyPair[i].getAttributes())) { failMsg = (i == 0 ? "Left" : "Right") + "pair attributes do not match"; } } else if (highestPair[i] != null || proxyPair[i] != null) { // Both highlights should be null otherwise they would not match failMsg = (i == 0 ? "Left" : "Right") + " highlight doesn't match"; } } } catch (Throwable e) { failMsg = e.getMessage(); } if (failMsg != null) { // Dump the layers System.out.println("Dumping layers:"); for (int i = 0; i < layers.length; i++) { System.out.println(" layer[" + i + "] = " + layerNames[i] + "{"); for (HighlightsSequence highlights = layers[i].getHighlights(0, 100); highlights.moveNext(); ) { Highlight h = copyCurrentHighlight(highlights); System.out.println(" " + dumpHighlight(h)); } System.out.println(" } End of layer[" + i + "] -----------------"); } System.out.println("Dumping proxy layer: {"); for (HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); proxyHighlights.moveNext(); ) { Highlight h = copyCurrentHighlight(proxyHighlights); System.out.println(" " + dumpHighlight(h)); } System.out.println("} End of proxy layer -----------------------"); // Dump the pair that failed System.out.println("highest pair (pos = " + pointer + ") : " + dumpHighlight(highestPair[0]) + ", " + dumpHighlight(highestPair[1])); System.out.println(" proxy pair (pos = " + pointer + ") : " + dumpHighlight(proxyPair[0]) + ", " + dumpHighlight(proxyPair[1])); fail(failMsg + " (position = " + pointer + ")"); } } } public void testEvents() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); ProxyHighlightsContainer chc = new ProxyHighlightsContainer(new HighlightsContainer [] { hsA, hsB }); Listener listener = new Listener(); chc.addHighlightsChangeListener(listener); hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet()); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 10, listener.lastEventStartOffset); assertEquals("Wrong change end offset", 20, listener.lastEventEndOffset); listener.reset(); hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet()); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 11, listener.lastEventStartOffset); assertEquals("Wrong change end offset", 12, listener.lastEventEndOffset); } public void testEvents2() { PlainDocument doc = new PlainDocument(); PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet()); hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet()); ProxyHighlightsContainer chc = new ProxyHighlightsContainer(); Listener listener = new Listener(); chc.addHighlightsChangeListener(listener); // changing delegate layers fires event covering 'all' offsets chc.setLayers(new HighlightsContainer [] { hsA, hsB }); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertEquals("Wrong change start offset", 0, listener.lastEventStartOffset); assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEventEndOffset); } private Highlight [] findPair(int offset, HighlightsSequence highlights) { Highlight left = null; Highlight right = null; for ( ; highlights.moveNext(); ) { if (highlights.getStartOffset() == highlights.getEndOffset()) { // ignore empty offsets continue; } if (offset > highlights.getStartOffset() && offset < highlights.getEndOffset()) { left = right = copyCurrentHighlight(highlights); } else if (offset == highlights.getEndOffset()) { left = copyCurrentHighlight(highlights); } else if (offset == highlights.getStartOffset()) { right = copyCurrentHighlight(highlights); } } return new Highlight [] { left, right }; } private Highlight copyCurrentHighlight(HighlightsSequence iterator) { return new Highlight( iterator.getStartOffset(), iterator.getEndOffset(), iterator.getAttributes() ); } private String dumpHighlight(Highlight h) { if (h == null) { return "< , , >"; } else { StringBuilder sb = new StringBuilder(); sb.append("<"); sb.append(h.getStartOffset()); sb.append(","); sb.append(h.getEndOffset()); sb.append(","); Enumeration en = h.getAttributes().getAttributeNames(); while (en.hasMoreElements()) { Object attrName = en.nextElement(); Object attrValue = h.getAttributes().getAttribute(attrName); sb.append("'"); sb.append(attrName.toString()); sb.append("' = '"); sb.append(attrValue == null ? "null" : attrValue.toString()); sb.append("'"); if (en.hasMoreElements()) { sb.append(", "); } } sb.append(">"); return sb.toString(); } } private PositionsBag createRandomBag(Document doc, String bagId) { PositionsBag bag = new PositionsBag(doc, false); Random rand = new Random(System.currentTimeMillis()); int attrIdx = 0; int startOffset = 0; int endOffset = 100; int maxGapSize = Math.max((int) (endOffset - startOffset) / 10, 1); int maxHighlightSize = Math.max((int) (endOffset - startOffset) / 2, 1); for (int pointer = startOffset + rand.nextInt(maxGapSize); pointer <= endOffset; ) { int highlightSize = rand.nextInt(maxHighlightSize); SimpleAttributeSet attributes = new SimpleAttributeSet(); attributes.addAttribute("AttrName-" + bagId + "-" + attrIdx, "AttrValue"); attrIdx++; if (pointer + highlightSize < endOffset) { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(pointer + highlightSize), attributes); } else { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(endOffset), attributes); } // move the pointer pointer += highlightSize + rand.nextInt(maxGapSize); } return bag; } private static final class Highlight { private int startOffset; private int endOffset; private AttributeSet attributes; public Highlight(int startOffset, int endOffset, AttributeSet attributes) { this.startOffset = startOffset; this.endOffset = endOffset; this.attributes = attributes; } public int getStartOffset() { return startOffset; } public void setStartOffset(int startOffset) { this.startOffset = startOffset; } public int getEndOffset() { return endOffset; } public void setEndOffset(int endOffset) { this.endOffset = endOffset; } public AttributeSet getAttributes() { return attributes; } public void setAttributes(AttributeSet attributes) { this.attributes = attributes; } } // End of H class private static final class Listener implements HighlightsChangeListener { public int eventsCnt = 0; public int lastEventStartOffset = 0; public int lastEventEndOffset = 0; public void highlightChanged(HighlightsChangeEvent event) { eventsCnt++; lastEventStartOffset = event.getStartOffset(); lastEventEndOffset = event.getEndOffset(); } public void reset() { eventsCnt = 0; lastEventStartOffset = 0; lastEventEndOffset = 0; } } // End of Listener class private static final class SimplePosition implements Position { private int offset; public SimplePosition(int offset) { this.offset = offset; } public int getOffset() { return offset; } } // End of SimplePosition class private void dumpHighlights(HighlightsSequence seq) { System.out.println("Dumping highlights from: " + seq + "{"); while(seq.moveNext()) { System.out.println("<" + seq.getStartOffset() + ", " + seq.getEndOffset() + ", " + seq.getAttributes() + ">"); } System.out.println("} --- End of Dumping highlights from: " + seq + " ---------------------"); } } File [added]: SyntaxHighlightingTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/SyntaxHighlightingTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 279 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.lib2.highlighting; import java.util.ConcurrentModificationException; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import junit.textui.TestRunner; import org.netbeans.api.lexer.Language; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenId; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.junit.NbTestCase; import org.netbeans.lib.lexer.test.simple.SimplePlainTokenId; import org.netbeans.lib.lexer.test.simple.SimpleTokenId; import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; import org.netbeans.spi.editor.highlighting.HighlightsSequence; /** * * @author vita */ public class SyntaxHighlightingTest extends NbTestCase { public static void main(String... args) { TestRunner.run(SyntaxHighlightingTest.class); } /** Creates a new instance of SyntaxHighlightingTest */ public SyntaxHighlightingTest(String name) { super(name); } public void testSimple() { checkText("+ - / * public", SimpleTokenId.language()); } public void testEmbedded() { checkText("/**//* this is a comment */", SimpleTokenId.language()); } public void testComplex() { checkText( "public /**/ ± private /** hello */ something /* this is a comment */ \"hi hi hi\" xyz ", SimpleTokenId.language()); } public void testNoPrologEpilogEmbedding() { checkText( "hello world 0-1-2-3-4-5-6-7-8-9-A-B-C-D-E-F Ooops", SimplePlainTokenId.language()); } public void testConcurrentModifications() throws BadLocationException { Document doc = createDocument(SimpleTokenId.language(), "NetBeans NetBeans NetBeans"); SyntaxHighlighting layer = new SyntaxHighlighting(doc); { HighlightsSequence hs = layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("There should be some highlights", hs.moveNext()); // Modify the document doc.insertString(0, "Hey", SimpleAttributeSet.EMPTY); try { hs.moveNext(); fail("ConcurrentModificationException has not been thrown from moveNext()"); } catch (ConcurrentModificationException e) { // pass } } { HighlightsSequence hs = layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("There should be some highlights", hs.moveNext()); // Modify the document doc.insertString(0, "Hey", SimpleAttributeSet.EMPTY); try { hs.getStartOffset(); fail("ConcurrentModificationException has not been thrown from getStartOffset()"); } catch (ConcurrentModificationException e) { // pass } } { HighlightsSequence hs = layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("There should be some highlights", hs.moveNext()); // Modify the document doc.insertString(0, "Hey", SimpleAttributeSet.EMPTY); try { hs.getEndOffset(); fail("ConcurrentModificationException has not been thrown from getEndOffset()"); } catch (ConcurrentModificationException e) { // pass } } { HighlightsSequence hs = layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("There should be some highlights", hs.moveNext()); // Modify the document doc.insertString(0, "Hey", SimpleAttributeSet.EMPTY); try { hs.getAttributes(); fail("ConcurrentModificationException has not been thrown from getAttributes()"); } catch (ConcurrentModificationException e) { // pass } } } public void testEvents() throws BadLocationException { final String text = "Hello !"; Document doc = createDocument(SimpleTokenId.language(), text); SyntaxHighlighting layer = new SyntaxHighlighting(doc); L listener = new L(); layer.addHighlightsChangeListener(listener); assertHighlights( TokenHierarchy.create(text, SimpleTokenId.language()).tokenSequence(), layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE), true, "" ); assertEquals("There should be no events", 0, listener.eventsCnt); final String addedText = "World"; doc.insertString(6, addedText, SimpleAttributeSet.EMPTY); assertEquals("Wrong number of events", 1, listener.eventsCnt); assertTrue("Wrong change start offset", 6 >= listener.lastStartOffset); assertTrue("Wrong change end offset", 6 + addedText.length() <= listener.lastEndOffset); } private void checkText(String text, Language lang) { System.out.println("Checking text: '" + text + "'\n"); Document doc = createDocument(lang, text); SyntaxHighlighting layer = new SyntaxHighlighting(doc); HighlightsSequence hs = layer.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); TokenHierarchy tokens = TokenHierarchy.create(text, lang); assertHighlights(tokens.tokenSequence(), hs, true, ""); assertFalse("Unexpected highlights at the end of the sequence", hs.moveNext()); System.out.println("------------------------\n"); } private Document createDocument(Language lang, String text) { try { DefaultStyledDocument doc = new DefaultStyledDocument(); doc.putProperty(Language.class, lang); doc.insertString(0, text, SimpleAttributeSet.EMPTY); return doc; } catch (BadLocationException e) { fail(e.getMessage()); return null; } } private void assertHighlights(TokenSequence ts, HighlightsSequence hs, boolean moveHs, String indent) { while (ts.moveNext()) { boolean hasHighlight; if (moveHs) { hasHighlight = hs.moveNext(); } else { hasHighlight = moveHs = true; } assertTrue("Wrong number of highlights", hasHighlight); System.out.println(indent + "Token : <" + ts.offset() + ", " + (ts.offset() + ts.token().length()) + ", '" + ts.token().text() + "', " + ts.token().id().name() + ">"); TokenSequence embeddedSeq = ts.embedded(); if (embeddedSeq == null) { System.out.println(indent + "Highlight: <" + hs.getStartOffset() + ", " + hs.getEndOffset() + ">"); assertEquals("Wrong starting offset", ts.offset(), hs.getStartOffset()); assertEquals("Wrong ending offset", ts.offset() + ts.token().length(), hs.getEndOffset()); // XXX: compare attributes as well } else { int prologueLength = embeddedPrologLength(ts, embeddedSeq); int epilogLength = embeddedEpilogLength(ts, embeddedSeq); if (prologueLength != -1 && epilogLength != -1) { if (prologueLength > 0) { System.out.println(indent + "Prolog : <" + hs.getStartOffset() + ", " + hs.getEndOffset() + ">"); assertEquals("Wrong starting offset", ts.offset(), hs.getStartOffset()); assertEquals("Wrong ending offset", ts.offset() + prologueLength, hs.getEndOffset()); // XXX: compare attributes as well } assertHighlights(ts.embedded(), hs, prologueLength > 0, indent + " "); if (epilogLength > 0) { assertTrue("Wrong number of highlights", hs.moveNext()); System.out.println(indent + "Epilog : <" + hs.getStartOffset() + ", " + hs.getEndOffset() + ">"); assertEquals("Wrong starting offset", ts.offset() + ts.token().length() - epilogLength, hs.getStartOffset()); assertEquals("Wrong ending offset", ts.offset() + ts.token().length(), hs.getEndOffset()); // XXX: compare attributes as well } } else { System.out.println(indent + "Highlight: <" + hs.getStartOffset() + ", " + hs.getEndOffset() + ">"); assertEquals("Wrong starting offset", ts.offset(), hs.getStartOffset()); assertEquals("Wrong ending offset", ts.offset() + ts.token().length(), hs.getEndOffset()); // XXX: compare attributes as well } } } } private int embeddedPrologLength( TokenSequence embeddingSeq, TokenSequence embeddedSeq) { if (embeddedSeq.moveFirst()) { return embeddedSeq.offset() - embeddingSeq.offset(); } else { return -1; } } private int embeddedEpilogLength( TokenSequence embeddingSeq, TokenSequence embeddedSeq) { if (embeddedSeq.moveLast()) { return (embeddingSeq.offset() + embeddingSeq.token().length()) - (embeddedSeq.offset() + embeddedSeq.token().length()); } else { return -1; } } private void dumpSequence(HighlightsSequence hs) { System.out.println("Dumping sequence: " + hs + " {"); while(hs.moveNext()) { System.out.println("<" + hs.getStartOffset() + ", " + hs.getEndOffset() + ">"); } System.out.println("} End of sequence: " + hs + " dump ------------"); } private static final class L implements HighlightsChangeListener { public int eventsCnt = 0; public int lastStartOffset; public int lastEndOffset; public void highlightChanged(HighlightsChangeEvent event) { eventsCnt++; lastStartOffset = event.getStartOffset(); lastEndOffset = event.getEndOffset(); } } // End of L class } Directory: /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/ =========================================================================== File [added]: AttributesUtilitiesTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/AttributesUtilitiesTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 151 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import java.awt.Color; import java.util.ArrayList; import java.util.HashMap; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import org.netbeans.junit.NbTestCase; /** * * @author Vita Stejskal */ public class AttributesUtilitiesTest extends NbTestCase { /** Creates a new instance of AttributesUtilitiesTest */ public AttributesUtilitiesTest(String name) { super(name); } public void testCreateImmutablePairs() { Object [] pairs = new Object [] { "key-1", "value-1", new HashMap(), new ArrayList(), new Integer(123456), new Character('.') }; AttributeSet as = AttributesUtilities.createImmutable(pairs); assertEquals("Wrong size of attribute set", pairs.length / 2, as.getAttributeCount()); for(int i = 0; i < pairs.length / 2; i++) { Object value = as.getAttribute(pairs[2 * i]); assertSame("Wrong attribute value", pairs[2 * i + 1], value); } } public void testCreateImmutableSets() { final Object SHARED_ATTR_KEY = "SHARED-ATTRIBUTE"; final Object SHARED_ATTR_FIRST_VALUE = new Color(0); SimpleAttributeSet setA = new SimpleAttributeSet(); SimpleAttributeSet setB = new SimpleAttributeSet(); SimpleAttributeSet setC = new SimpleAttributeSet(); setA.addAttribute(new ArrayList(2), new Long(1234)); setA.addAttribute(new HashMap(2), "AAA"); setA.addAttribute(new Object(), SimpleAttributeSet.EMPTY); setA.addAttribute(new Double("1.1"), setA); setA.addAttribute(SHARED_ATTR_KEY, SHARED_ATTR_FIRST_VALUE); setB.addAttribute("set-2/attr-1", "set-2/attr-1/value-1"); setB.addAttribute(new Integer(2), new Character(',')); setC.addAttribute("set-3/attr-1", "set-3/attr-1/value-1"); setC.addAttribute(SHARED_ATTR_KEY, "set-3/attr-2/value-2"); setC.addAttribute("set-3/attr-3", "set-3/attr-3/value-3"); AttributeSet as = AttributesUtilities.createImmutable(setA, setB, setC); // Check the initial contents assertEquals("Wrong number of attributes", setA.getAttributeCount() + setB.getAttributeCount() + setC.getAttributeCount() - 1, as.getAttributeCount()); assertTrue("Doesn't contain setA attributes", as.containsAttributes(setA)); assertTrue("Doesn't contain setB attributes", as.containsAttributes(setB)); assertTrue("Wrong setC attr-1", as.containsAttribute("set-3/attr-1", "set-3/attr-1/value-1")); assertTrue("Wrong setC attr-3", as.containsAttribute("set-3/attr-3", "set-3/attr-3/value-3")); assertTrue("Wrong SHARED-ATTRIBUTE", as.containsAttribute(SHARED_ATTR_KEY, SHARED_ATTR_FIRST_VALUE)); // Check that mutations are not propagated setA.removeAttribute(SHARED_ATTR_KEY); setC.removeAttribute(SHARED_ATTR_KEY); assertFalse("setA still contains shared attribute", setA.isDefined(SHARED_ATTR_KEY)); assertFalse("setC still contains shared attribute", setC.isDefined(SHARED_ATTR_KEY)); assertTrue("AS should not be modified", as.isDefined(SHARED_ATTR_KEY)); assertTrue("AS should not be modified", as.containsAttribute(SHARED_ATTR_KEY, SHARED_ATTR_FIRST_VALUE)); } public void testCreateComposite() { final Object SHARED_ATTR_KEY = "SHARED-ATTRIBUTE"; final Object SHARED_ATTR_LAST_VALUE = new Color(0); SimpleAttributeSet setA = new SimpleAttributeSet(); SimpleAttributeSet setB = new SimpleAttributeSet(); SimpleAttributeSet setC = new SimpleAttributeSet(); setA.addAttribute("set-1/attr-1", "set-1/attr-1/value-1"); setA.addAttribute(SHARED_ATTR_KEY, "set-1/attr-2/value-2"); setA.addAttribute("set-1/attr-3", "set-1/attr-3/value-3"); setB.addAttribute("set-2/attr-1", "set-2/attr-1/value-1"); setB.addAttribute(new Integer(2), new Character(',')); setC.addAttribute(new ArrayList(2), new Long(1234)); setC.addAttribute(new HashMap(2), "AAA"); setC.addAttribute(new Object(), SimpleAttributeSet.EMPTY); setC.addAttribute(new Double("1.1"), setA); setC.addAttribute(SHARED_ATTR_KEY, SHARED_ATTR_LAST_VALUE); AttributeSet as = AttributesUtilities.createComposite(setC, setB, setA); // Check the initial contents assertEquals("Wrong number of attributes", setA.getAttributeCount() + setB.getAttributeCount() + setC.getAttributeCount() - 1, as.getAttributeCount()); assertTrue("Doesn't contain setC attributes", as.containsAttributes(setC)); assertTrue("Doesn't contain setB attributes", as.containsAttributes(setB)); assertTrue("Wrong setA attr-1", as.containsAttribute("set-1/attr-1", "set-1/attr-1/value-1")); assertTrue("Wrong setA attr-3", as.containsAttribute("set-1/attr-3", "set-1/attr-3/value-3")); assertTrue("Wrong SHARED-ATTRIBUTE", as.containsAttribute(SHARED_ATTR_KEY, SHARED_ATTR_LAST_VALUE)); // Check that mutations __are__ propagated setA.removeAttribute(SHARED_ATTR_KEY); setC.removeAttribute(SHARED_ATTR_KEY); assertFalse("setA still contains shared attribute", setA.isDefined(SHARED_ATTR_KEY)); assertFalse("setC still contains shared attribute", setC.isDefined(SHARED_ATTR_KEY)); assertFalse("AS still contains shared attribute", as.isDefined(SHARED_ATTR_KEY)); String newSharedValue = "HA!"; setA.addAttribute(SHARED_ATTR_KEY, newSharedValue); assertTrue("AS doesn't show the added attribute", as.containsAttribute(SHARED_ATTR_KEY, newSharedValue)); // Check the clone is the same AttributeSet clone = as.copyAttributes(); assertTrue("as.copyAttributes() != as", clone.isEqual(as)); assertTrue("as != as.copyAttributes()", as.isEqual(clone)); } } File [added]: ZOrderTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/ZOrderTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 214 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting; import org.netbeans.junit.NbTestCase; import org.openide.util.TopologicalSortException; /** * * @author vita */ public class ZOrderTest extends NbTestCase { /** Creates a new instance of ZOrderTest */ public ZOrderTest(String name) { super(name); } public void testOrder() throws Exception { ZOrder zOrderA = ZOrder.DEFAULT_RACK; ZOrder zOrderB = ZOrder.above("layerA"); HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerB", zOrderB), simpleLayer("layerA", zOrderA), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); assertSame("Wrong order", layers[0], sortedLayers[1]); assertSame("Wrong order", layers[1], sortedLayers[0]); } public void testOrder2() throws Exception { ZOrder zOrderA = ZOrder.DEFAULT_RACK.belowLayers("layerB"); ZOrder zOrderB = ZOrder.DEFAULT_RACK.aboveLayers("layerA").belowLayers("layerC"); ZOrder zOrderC = ZOrder.DEFAULT_RACK.aboveLayers("layerB").belowLayers("layerD"); ZOrder zOrderD = ZOrder.DEFAULT_RACK.aboveLayers("layerC").belowLayers("layerE"); ZOrder zOrderE = ZOrder.DEFAULT_RACK.aboveLayers("layerD"); HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerD", zOrderD), simpleLayer("layerC", zOrderC), simpleLayer("layerA", zOrderA), simpleLayer("layerE", zOrderE), simpleLayer("layerB", zOrderB), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); char ch = 'A'; for (int i = 0; i < sortedLayers.length; i++) { String expectedLayerName = "layer" + ch++; assertEquals("Wrong order", expectedLayerName, sortedLayers[i].getLayerTypeId()); } } public void testCreation() { ZOrder zOrder = ZOrder.DEFAULT_RACK; assertNotNull("ZOrder.DEFAULT", zOrder); assertEquals("DEFAULT.layersAbove", 1, zOrder.layersAbove.size()); assertEquals("DEFAULT.layersBelow", 1, zOrder.layersBelow.size()); zOrder = ZOrder.BOTTOM_RACK; assertNotNull("ZOrder.BOTTOM", zOrder); assertEquals("BOTTOM.layersAbove", 1, zOrder.layersAbove.size()); assertEquals("BOTTOM.layersBelow", 0, zOrder.layersBelow.size()); zOrder = ZOrder.TOP_RACK; assertNotNull("ZOrder.TOP", zOrder); assertEquals("TOP.layersAbove", 0, zOrder.layersAbove.size()); assertEquals("TOP.layersBelow", 1, zOrder.layersBelow.size()); zOrder = ZOrder.above("layerA"); assertEquals("layersAbove should be empty", 0, zOrder.layersAbove.size()); assertEquals("Wrong number of layersBelow", 1, zOrder.layersBelow.size()); assertEquals("Wrong layersBelow", "layerA", zOrder.layersBelow.iterator().next()); zOrder = ZOrder.below("layerA"); assertEquals("layersBelow should be empty", 0, zOrder.layersBelow.size()); assertEquals("Wrong number of layersAbove", 1, zOrder.layersAbove.size()); assertEquals("Wrong layersAbove", "layerA", zOrder.layersAbove.iterator().next()); zOrder = ZOrder.DEFAULT_RACK.aboveLayers("layerA"); assertNotSame("ZOrder was not cloned", ZOrder.DEFAULT_RACK, zOrder); assertEquals("layersAbove should be empty", 1, zOrder.layersAbove.size()); assertEquals("Wrong number of layersBelow", 2, zOrder.layersBelow.size()); assertTrue("Wrong layersBelow", zOrder.layersBelow.contains("layerA")); zOrder = ZOrder.DEFAULT_RACK.belowLayers("layerA"); assertNotSame("ZOrder was not cloned", ZOrder.DEFAULT_RACK, zOrder); assertEquals("layersBelow should be empty", 1, zOrder.layersBelow.size()); assertEquals("Wrong number of layersAbove", 2, zOrder.layersAbove.size()); assertTrue("Wrong layersAbove", zOrder.layersAbove.contains("layerA")); zOrder = ZOrder.DEFAULT_RACK.aboveLayers("layerA").belowLayers("layerB"); assertEquals("Wrong number of layersAbove", 2, zOrder.layersAbove.size()); assertTrue("Wrong layersAbove", zOrder.layersAbove.contains("layerB")); assertEquals("Wrong number of layersBelow", 2, zOrder.layersBelow.size()); assertTrue("Wrong layersBelow", zOrder.layersBelow.contains("layerA")); } public void testTop() throws TopologicalSortException { ZOrder zOrderA = ZOrder.TOP_RACK; ZOrder zOrderB = ZOrder.above("layerA"); HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerB", zOrderB), simpleLayer("layerA", zOrderA), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); assertSame("Wrong order", layers[0], sortedLayers[1]); assertSame("Wrong order", layers[1], sortedLayers[0]); } public void testBottom() throws TopologicalSortException { ZOrder zOrderA = ZOrder.BOTTOM_RACK; ZOrder zOrderB = ZOrder.below("layerA"); HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerA", zOrderA), simpleLayer("layerB", zOrderB), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); assertSame("Wrong order", layers[0], sortedLayers[1]); assertSame("Wrong order", layers[1], sortedLayers[0]); } public void testRacks() throws TopologicalSortException { HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerE", ZOrder.SHOW_OFF_RACK), simpleLayer("layerC", ZOrder.CARET_RACK), simpleLayer("layerF", ZOrder.TOP_RACK), simpleLayer("layerA", ZOrder.BOTTOM_RACK), simpleLayer("layerD", ZOrder.DEFAULT_RACK), simpleLayer("layerB", ZOrder.SYNTAX_RACK), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); char ch = 'A'; for(int i = 0; i < sortedLayers.length; i++) { assertEquals("Wrong order", "layer" + ch, sortedLayers[i].getLayerTypeId()); ch++; } } public void testComplex() throws TopologicalSortException { ZOrder zOrderA = ZOrder.BOTTOM_RACK; ZOrder zOrderB = ZOrder.DEFAULT_RACK.aboveLayers("layerA").belowLayers("layerC"); ZOrder zOrderC = ZOrder.DEFAULT_RACK.aboveLayers("layerB").belowLayers("layerD"); ZOrder zOrderD = ZOrder.DEFAULT_RACK.aboveLayers("layerC").belowLayers("layerE"); ZOrder zOrderE = ZOrder.TOP_RACK; HighlightsLayer [] layers = new HighlightsLayer [] { simpleLayer("layerD", zOrderD), simpleLayer("layerC", zOrderC), simpleLayer("layerA", zOrderA), simpleLayer("layerE", zOrderE), simpleLayer("layerB", zOrderB), }; HighlightsLayer [] sortedLayers = ZOrder.sort(layers); assertNotNull("Sorted layers should not be null", sortedLayers); assertEquals("Wrong size of sortedLayers array", layers.length, sortedLayers.length); char ch = 'A'; for (int i = 0; i < sortedLayers.length; i++) { String expectedLayerName = "layer" + ch++; assertEquals("Wrong order", expectedLayerName, sortedLayers[i].getLayerTypeId()); } } private HighlightsLayer simpleLayer(String layerId, ZOrder zOrder) { return HighlightsLayer.create(layerId, zOrder, true, null); } } Directory: /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/ ======================================================================================= File [added]: PositionsBagFindHighlightTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/PositionsBagFindHighlightTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 71 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.performance; import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import junit.framework.TestSuite; import org.netbeans.junit.NbTestCase; import org.netbeans.junit.NbTestSuite; import org.netbeans.spi.editor.highlighting.support.PositionsBag; import org.netbeans.spi.editor.highlighting.HighlightsSequence; /** * * @author vita */ public class PositionsBagFindHighlightTest extends NbTestCase { public static TestSuite suite() { return NbTestSuite.speedSuite(PositionsBagFindHighlightTest.class, 2, 3); } private int cnt = 0; private PositionsBag bag = null; private int startOffset; private int endOffset; /** Creates a new instance of PerfTest */ public PositionsBagFindHighlightTest(String name) { super(name); } protected void setUp() { cnt = this.getTestNumber(); bag = new PositionsBag(new PlainDocument(), false); for(int i = 0; i < cnt; i++) { bag.addHighlight(new SimplePosition(i * 10), new SimplePosition(i * 10 + 5), SimpleAttributeSet.EMPTY); } startOffset = 10 * cnt / 5 - 1; endOffset = 10 * (cnt/ 5 + 1) - 1; System.out.println("cnt = " + cnt + " : startOffset = " + startOffset + " : endOffset = " + endOffset); } public void testFindHighlight10() { HighlightsSequence seq = bag.getHighlights(startOffset, endOffset); } public void testFindHighlight10000() { HighlightsSequence seq = bag.getHighlights(startOffset, endOffset); } } File [added]: PositionsBagMemoryTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/PositionsBagMemoryTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 159 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.performance; import java.lang.reflect.Method; import java.util.Collections; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.junit.MemoryFilter; import org.netbeans.junit.NbTestCase; import org.netbeans.lib.editor.util.GapList; import org.netbeans.spi.editor.highlighting.*; import org.netbeans.spi.editor.highlighting.support.PositionsBag; /** * * @author Vita Stejskal */ public class PositionsBagMemoryTest extends NbTestCase { private static final int CNT = 1000; /** Creates a new instance of HighlightsBagPerformanceTest */ public PositionsBagMemoryTest(String name) { super(name); } public void testMemoryConsumptionBestCase() { checkMemoryConsumption(false, true); } public void testMemoryConsumptionWorstCase() { checkMemoryConsumption(false, false); } public void testIteratorMemoryConsumptionBestCase() { checkIteratorMemoryConsumption(false, true); } public void testIteratorMemoryConsumptionWorstCase() { checkIteratorMemoryConsumption(false, false); } public void testMergingBagMemoryConsumptionBestCase() { checkMemoryConsumption(true, true); } public void testMergingBagMemoryConsumptionWorstCase() { checkMemoryConsumption(true, false); } public void testMergingBagIteratorMemoryConsumptionBestCase() { checkIteratorMemoryConsumption(true, true); } public void testMergingBagIteratorMemoryConsumptionWorst() { checkIteratorMemoryConsumption(true, false); } private void checkMemoryConsumption(boolean merging, boolean bestCase) { PositionsBag bag = new PositionsBag(new PlainDocument(), merging); for(int i = 0; i < CNT; i++) { if (bestCase) { bag.addHighlight(new SimplePosition(i * 10), new SimplePosition((i + 1) * 10), SimpleAttributeSet.EMPTY); } else { bag.addHighlight(new SimplePosition(i * 10), new SimplePosition(i* 10 + 5), SimpleAttributeSet.EMPTY); } } compact(bag); assertSize("PositionsBag of " + CNT + " highlights " + (bestCase ? "(best case)" : "(worst case)"), Collections.singleton(bag), bestCase ? 8500 : 16500, new MF()); } private void checkIteratorMemoryConsumption(boolean merging, boolean bestCase) { PositionsBag bag = new PositionsBag(new PlainDocument(), merging); for(int i = 0; i < CNT; i++) { if (bestCase) { bag.addHighlight(new SimplePosition(i * 10), new SimplePosition((i + 1) * 10), SimpleAttributeSet.EMPTY); } else { bag.addHighlight(new SimplePosition(i * 10), new SimplePosition(i* 10 + 5), SimpleAttributeSet.EMPTY); } } compact(bag); HighlightsSequence sequence = bag.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); // Do not iterate through the whole sequence, otherwise it will discard its contents for(int i = 0; i < CNT - 1; i++) { boolean hasHighlight = sequence.moveNext(); assertTrue("Wrong number of highlights in the sequence; found only " + i, hasHighlight); assertEquals("Wrong start offset of " + i + ". highlight", i * 10, sequence.getStartOffset()); assertEquals("Wrong end offset of " + i + ". highlight", bestCase ? (i + 1) * 10 : i * 10 + 5, sequence.getEndOffset()); assertSame("Wrong attributes of " + i + ". highlight", SimpleAttributeSet.EMPTY, sequence.getAttributes()); } assertSize("HighlightsSequence of " + CNT + " highlights " + (bestCase ? "(best case)" : "(worst case)"), Collections.singleton(sequence), bestCase ? 8500 : 16500, new MF()); } @SuppressWarnings("unchecked") private void compact(PositionsBag bag) { try { Method m = bag.getClass().getDeclaredMethod("getMarks"); m.setAccessible(true); GapList marks = (GapList) m.invoke(bag); marks.trimToSize(); m = bag.getClass().getDeclaredMethod("getAttributes"); m.setAccessible(true); GapList attributes = (GapList) m.invoke(bag); attributes.trimToSize(); } catch (Exception e) { AssertionError ae = new AssertionError(e.getMessage()); ae.initCause(e); throw ae; } } private static final class MF implements MemoryFilter { public boolean reject(Object obj) { if (Position.class.isAssignableFrom(obj.getClass()) || AttributeSet.class.isAssignableFrom(obj.getClass()) || Document.class.isAssignableFrom(obj.getClass())) { return true; } else { return false; } } } // End of MF class } File [added]: SimplePosition.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/SimplePosition.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 38 --------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.performance; import javax.swing.text.Position; /** * * @author vita */ public final class SimplePosition implements Position { private int offset; public SimplePosition(int offset) { this.offset = offset; } public int getOffset() { return offset; } } File [added]: SyntaxHighlightingTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/SyntaxHighlightingTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 123 ---------------- /* * SyntaxHighlightingTest.java * * Created on January 18, 2007, 9:47 PM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package org.netbeans.spi.editor.highlighting.performance; import java.io.FileInputStream; import java.io.InputStream; import java.io.File; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.lexer.Language; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.editor.lib2.highlighting.SyntaxHighlighting; import org.netbeans.spi.editor.highlighting.HighlightsSequence; /** * * @author vita */ public class SyntaxHighlightingTest extends NbTestCase { /** Creates a new instance of SyntaxHighlightingTest */ public SyntaxHighlightingTest(String name) { super(name); } private Document document; protected void setUp() { // Create and load document Document d = new PlainDocument(); try { File dataFolder = new File(getClass().getResource("data").toURI()); InputStream io = new FileInputStream(new File(dataFolder, "VeryBigClassThatWasStolenAndDoesNotCompile-java")); try { byte [] buffer = new byte [1024]; int size; while (0 < (size = io.read(buffer, 0, buffer.length))) { String s = new String(buffer, 0, size); d.insertString(d.getLength(), s, SimpleAttributeSet.EMPTY); } } finally { io.close(); } } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } Language javaLang = JavaTokenId.language(); d.putProperty(Language.class, javaLang); d.putProperty("mimeType", javaLang.mimeType()); this.document = d; } public void testLexer() { long timestamp1, timestamp2; timestamp1 = System.currentTimeMillis(); TokenHierarchy th = TokenHierarchy.get(document); timestamp2 = System.currentTimeMillis(); System.out.println("TokenHierarchy.get(document) took " + (timestamp2 - timestamp1) + " msecs."); timestamp1 = System.currentTimeMillis(); TokenSequence ts = th.tokenSequence(); timestamp2 = System.currentTimeMillis(); System.out.println("TokenHierarchy.tokenSequence() took " + (timestamp2 - timestamp1) + " msecs."); timestamp1 = System.currentTimeMillis(); iterateOver(ts); timestamp2 = System.currentTimeMillis(); System.out.println("Iterating through TokenSequence took " + (timestamp2 - timestamp1) + " msecs."); timestamp1 = System.currentTimeMillis(); SyntaxHighlighting layer = new SyntaxHighlighting(document); timestamp2 = System.currentTimeMillis(); System.out.println("SyntaxHighlighting creation took " + (timestamp2 - timestamp1) + " msecs."); timestamp1 = System.currentTimeMillis(); HighlightsSequence hs = layer.getHighlights(0, Integer.MAX_VALUE); timestamp2 = System.currentTimeMillis(); System.out.println("SyntaxHighlighting.getHighlights() took " + (timestamp2 - timestamp1) + " msecs."); timestamp1 = System.currentTimeMillis(); iterateOver(hs); timestamp2 = System.currentTimeMillis(); System.out.println("Iterating through HighlightsSequence took " + (timestamp2 - timestamp1) + " msecs."); } private void iterateOver(TokenSequence ts) { for( ; ts.moveNext(); ) { String name = ts.token().id().name(); assertNotNull("Token name must not be null", name); TokenSequence embedded = ts.embedded(); if (embedded != null) { iterateOver(embedded); } } } private void iterateOver(HighlightsSequence hs) { for( ; hs.moveNext(); ) { AttributeSet attribs = hs.getAttributes(); assertNotNull("AttributeSet must not be null", attribs); } } } Directory: /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/data/ ============================================================================================ File [added]: VeryBigClassThatWasStolenAndDoesNotCompile-java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/performance/data/VeryBigClassThatWasStolenAndDoesNotCompile-java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 5334 ----------------- /* * @(#)JComponent.java 2.247 05/05/27 * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package org.netbeans.spi.editor.highlighting.performance.data; import java.util.Hashtable; import java.util.Dictionary; import java.util.Enumeration; import java.util.Locale; import java.util.Vector; import java.util.EventListener; import java.util.Set; import java.util.TreeSet; import java.util.Map; import java.util.HashMap; import java.awt.*; import java.awt.event.*; import java.awt.image.VolatileImage; import java.awt.Graphics2D; import java.awt.peer.LightweightPeer; import java.awt.dnd.DropTarget; import java.awt.font.FontRenderContext; import java.beans.*; import java.applet.Applet; import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.io.ObjectInputValidation; import java.io.InvalidObjectException; import java.lang.reflect.Method; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.accessibility.*; import com.sun.java.swing.SwingUtilities2; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.CellRendererPane; import javax.swing.ComponentInputMap; import javax.swing.InputMap; import javax.swing.InputVerifier; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.JToolTip; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.Popup; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.TransferHandler; import sun.font.FontDesignMetrics; /** * The base class for all Swing components except top-level containers. * To use a component that inherits from JComponent, * you must place the component in a containment hierarchy * whose root is a top-level Swing container. * Top-level Swing containers -- * such as JFrame, JDialog, * and JApplet -- * are specialized components * that provide a place for other Swing components to paint themselves. * For an explanation of containment hierarchies, see * Swing Components and the Containment Hierarchy, * a section in The Java Tutorial. * *

* The JComponent class provides: *

    *
  • The base class for both standard and custom components * that use the Swing architecture. *
  • A "pluggable look and feel" (L&F) that can be specified by the * programmer or (optionally) selected by the user at runtime. * The look and feel for each component is provided by a * UI delegate -- an object that descends from * {@link javax.swing.plaf.ComponentUI}. * See How * to Set the Look and Feel * in The Java Tutorial * for more information. *
  • Comprehensive keystroke handling. * See the document Keyboard * Bindings in Swing, * an article in The Swing Connection, * for more information. *
  • Support for tool tips -- * short descriptions that pop up when the cursor lingers * over a component. * See How * to Use Tool Tips * in The Java Tutorial * for more information. *
  • Support for accessibility. * JComponent contains all of the methods in the * Accessible interface, * but it doesn't actually implement the interface. That is the * responsibility of the individual classes * that extend JComponent. *
  • Support for component-specific properties. * With the {@link #putClientProperty} * and {@link #getClientProperty} methods, * you can associate name-object pairs * with any object that descends from JComponent. *
  • An infrastructure for painting * that includes double buffering and support for borders. * For more information see Painting and * How * to Use Borders, * both of which are sections in The Java Tutorial. *
* For more information on these subjects, see the * Swing package description * and The Java Tutorial section * The JComponent Class. *

* JComponent and its subclasses document default values * for certain properties. For example, JTable documents the * default row height as 16. Each JComponent subclass * that has a ComponentUI will create the * ComponentUI as part of its constructor. In order * to provide a particular look and feel each * ComponentUI may set properties back on the * JComponent that created it. For example, a custom * look and feel may require JTables to have a row * height of 24. The documented defaults are the value of a property * BEFORE the ComponentUI has been installed. If you * need a specific value for a particular property you should * explicitly set it. *

* In release 1.4, the focus subsystem was rearchitected. * For more information, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeansTM * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. * * @see KeyStroke * @see Action * @see #setBorder * @see #registerKeyboardAction * @see JOptionPane * @see #setDebugGraphicsOptions * @see #setToolTipText * @see #setAutoscrolls * * @version 2.130 07/09/99 * @author Hans Muller * @author Arnaud Weber */ public abstract class VeryBigClassThatWasStolenAndDoesNotCompile extends Container implements Serializable { /** * @see #getUIClassID * @see #writeObject */ private static final String uiClassID = "ComponentUI"; /** * Key used in client properties for the AncestorNotifier. */ private static final StringBuffer ANCESTOR_NOTIFIER_KEY = new StringBuffer( "AncestorNotifier"); /** * Key used in client properties for the TransferHandler. */ private static final StringBuffer TRANSFER_HANDLER_KEY = new StringBuffer( "TransferHandler"); /** * Key used in client properties for the InputVerifier. */ private static final StringBuffer INPUT_VERIFIER_KEY = new StringBuffer( "InputVerifier"); /** * @see #readObject */ private static final Hashtable readObjectCallbacks = new Hashtable(1); /** * Keys to use for forward focus traversal when the JComponent is * managing focus. */ private static Set managingFocusForwardTraversalKeys; /** * Keys to use for backward focus traversal when the JComponent is * managing focus. */ private static Set managingFocusBackwardTraversalKeys; /** * Indicates if we should register a DropTarget for a * non-null TransferHandler. Use * getSuppressDropTarget to check. */ private static boolean suppressDropSupport; /** * Indiciates if we've checked the system property for suppressing * drop support. */ private static boolean checkedSuppressDropSupport; // Following are the possible return values from getObscuredState. private static final int NOT_OBSCURED = 0; private static final int PARTIALLY_OBSCURED = 1; private static final int COMPLETELY_OBSCURED = 2; /** * Set to true when DebugGraphics has been loaded. */ static boolean DEBUG_GRAPHICS_LOADED; /** * Anti-aliased FontMetrics. */ private static final Map aaFontMap; /* The following fields support set methods for the corresponding * java.awt.Component properties. */ private boolean isAlignmentXSet; private float alignmentX; private boolean isAlignmentYSet; private float alignmentY; /** * Backing store for JComponent properties and listeners */ /** The look and feel delegate for this component. */ protected transient ComponentUI ui; /** A list of event listeners for this component. */ protected EventListenerList listenerList = new EventListenerList(); private transient ArrayTable clientProperties; private VetoableChangeSupport vetoableChangeSupport; /** * Whether or not autoscroll has been enabled. */ private boolean autoscrolls; private Border border; private int flags; /* Input verifier for this component */ private InputVerifier inputVerifier = null; private boolean verifyInputWhenFocusTarget = true; /** * Set in _paintImmediately. * Will indicate the child that initiated the painting operation. * If paintingChild is opaque, no need to paint * any child components after paintingChild. * Test used in paintChildren. */ transient Component paintingChild; /** * Constant used for registerKeyboardAction that * means that the command should be invoked when * the component has the focus. */ public static final int WHEN_FOCUSED = 0; /** * Constant used for registerKeyboardAction that * means that the command should be invoked when the receiving * component is an ancestor of the focused component or is * itself the focused component. */ public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1; /** * Constant used for registerKeyboardAction that * means that the command should be invoked when * the receiving component is in the window that has the focus * or is itself the focused component. */ public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** * Constant used by some of the APIs to mean that no condition is defined. */ public static final int UNDEFINED_CONDITION = -1; /** * The key used by JComponent to access keyboard bindings. */ private static final String KEYBOARD_BINDINGS_KEY = "_KeyboardBindings"; /** * An array of KeyStrokes used for * WHEN_IN_FOCUSED_WINDOW are stashed * in the client properties under this string. */ private static final String WHEN_IN_FOCUSED_WINDOW_BINDINGS = "_WhenInFocusedWindow"; /** * The comment to display when the cursor is over the component, * also known as a "value tip", "flyover help", or "flyover label". */ public static final String TOOL_TIP_TEXT_KEY = "ToolTipText"; private static final String NEXT_FOCUS = "nextFocus"; /** * JPopupMenu assigned to this component * and all of its childrens */ private JPopupMenu popupMenu; /** Private flags **/ private static final int IS_DOUBLE_BUFFERED = 0; private static final int ANCESTOR_USING_BUFFER = 1; private static final int IS_PAINTING_TILE = 2; private static final int IS_OPAQUE = 3; private static final int KEY_EVENTS_ENABLED = 4; private static final int FOCUS_INPUTMAP_CREATED = 5; private static final int ANCESTOR_INPUTMAP_CREATED = 6; private static final int WIF_INPUTMAP_CREATED = 7; private static final int ACTIONMAP_CREATED = 8; private static final int CREATED_DOUBLE_BUFFER = 9; // bit 10 is free private static final int IS_PRINTING = 11; private static final int IS_PRINTING_ALL = 12; private static final int IS_REPAINTING = 13; /** Bits 14-21 are used to handle nested writeObject calls. **/ private static final int WRITE_OBJ_COUNTER_FIRST = 14; private static final int RESERVED_1 = 15; private static final int RESERVED_2 = 16; private static final int RESERVED_3 = 17; private static final int RESERVED_4 = 18; private static final int RESERVED_5 = 19; private static final int RESERVED_6 = 20; private static final int WRITE_OBJ_COUNTER_LAST = 21; private static final int REQUEST_FOCUS_DISABLED = 22; private static final int INHERITS_POPUP_MENU = 23; private static final int OPAQUE_SET = 24; private static final int AUTOSCROLLS_SET = 25; private static final int FOCUS_TRAVERSAL_KEYS_FORWARD_SET = 26; private static final int FOCUS_TRAVERSAL_KEYS_BACKWARD_SET = 27; /** * Whether or not some JComponent is calling into an InputVerifier. */ private static boolean inInputVerifier; /** * Temporary rectangles. */ private static java.util.List tempRectangles = new java.util.ArrayList(11); /** Used for WHEN_FOCUSED bindings. */ private InputMap focusInputMap; /** Used for WHEN_ANCESTOR_OF_FOCUSED_COMPONENT bindings. */ private InputMap ancestorInputMap; /** Used for WHEN_IN_FOCUSED_KEY bindings. */ private ComponentInputMap windowInputMap; /** ActionMap. */ private ActionMap actionMap; /** Key used to store the default locale in an AppContext **/ private static final String defaultLocale = "JComponent.defaultLocale"; /** * Whether or not this component should turn on anti-aliased text * rendering. */ private boolean aaText; static { aaFontMap = new HashMap(); } /** * Returns the Set of KeyStrokes to use if the component * is managing focus for forward focus traversal. */ static Set getManagingFocusForwardTraversalKeys() { if (managingFocusForwardTraversalKeys == null) { managingFocusForwardTraversalKeys = new TreeSet(); managingFocusForwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_MASK)); } return managingFocusForwardTraversalKeys; } /** * Returns the Set of KeyStrokes to use if the component * is managing focus for backward focus traversal. */ static Set getManagingFocusBackwardTraversalKeys() { if (managingFocusBackwardTraversalKeys == null) { managingFocusBackwardTraversalKeys = new TreeSet(); managingFocusBackwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK)); } return managingFocusBackwardTraversalKeys; } /** * Returns true if setTransferHandler should install * a DropTarget. */ private static boolean getSuppressDropTarget() { if (!checkedSuppressDropSupport) { Boolean b = (Boolean)java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { String value = System.getProperty( "suppressSwingDropSupport"); if (value != null) { return Boolean.valueOf(value); } return Boolean.FALSE; } } ); suppressDropSupport = b.booleanValue(); checkedSuppressDropSupport = true; } return suppressDropSupport; } private static Rectangle fetchRectangle() { synchronized(tempRectangles) { Rectangle rect; int size = tempRectangles.size(); if (size > 0) { rect = (Rectangle)tempRectangles.remove(size - 1); } else { rect = new Rectangle(0, 0, 0, 0); } return rect; } } private static void recycleRectangle(Rectangle rect) { synchronized(tempRectangles) { tempRectangles.add(rect); } } /** * Sets whether or not getComponentPopupMenu should delegate * to the parent if this component does not have a JPopupMenu * assigned to it. *

* The default value for this is false, but some JComponent * subclasses that are implemented as a number of JComponents * may set this to true. *

* This is a bound property. * * @param value whether or not the JPopupMenu is inherited * @see #setComponentPopupMenu * @beaninfo * bound: true * description: Whether or not the JPopupMenu is inherited * @since 1.5 */ public void setInheritsPopupMenu(boolean value) { setFlag(INHERITS_POPUP_MENU, value); } /** * Returns true if the JPopupMenu should be inherited from the parent. * * @see #setComponentPopupMenu * @since 1.5 */ public boolean getInheritsPopupMenu() { return getFlag(INHERITS_POPUP_MENU); } /** * Sets the JPopupMenu for this JComponent. * The UI is responsible for registering bindings and adding the necessary * listeners such that the JPopupMenu will be shown at * the appropriate time. When the JPopupMenu is shown * depends upon the look and feel: some may show it on a mouse event, * some may enable a key binding. *

* If popup is null, and getInheritsPopupMenu * returns true, then getComponentPopupMenu will be delegated * to the parent. This provides for a way to make all child components * inherit the popupmenu of the parent. *

* This is a bound property. * * @param popup - the popup that will be assigned to this component * may be null * @see #getComponentPopupMenu * @beaninfo * bound: true * preferred: true * description: Popup to show * @since 1.5 */ public void setComponentPopupMenu(JPopupMenu popup) { if(popup != null && isLightweight()) { enableEvents(AWTEvent.MOUSE_EVENT_MASK); } this.popupMenu = popup; } /** * Returns JPopupMenu that assigned for this component. * If this component does not have a JPopupMenu assigned * to it and getInheritsPopupMenu is true, this * will return getParent().getComponentPopupMenu() (assuming * the parent is valid.) * * @return JPopupMenu assigned for this component * or null if no popup assigned * @see #setComponentPopupMenu * @since 1.5 */ public JPopupMenu getComponentPopupMenu() { if(!getInheritsPopupMenu()) { return popupMenu; } if(popupMenu == null) { // Search parents for its popup Container parent = getParent(); while (parent != null) { if(parent instanceof JComponent) { return ((JComponent)parent).getComponentPopupMenu(); } if(parent instanceof Window || parent instanceof Applet) { // Reached toplevel, break and return null break; } parent = parent.getParent(); } return null; } return popupMenu; } /** * Default JComponent constructor. This constructor does * very little initialization beyond calling the Container * constructor. For example, the initial layout manager is * null. It does, however, set the component's locale * property to the value returned by * JComponent.getDefaultLocale. * * @see #getDefaultLocale */ public VeryBigClassThatWasStolenAndDoesNotCompile { super(); // We enable key events on all JComponents so that accessibility // bindings will work everywhere. This is a partial fix to BugID // 4282211. enableEvents(AWTEvent.KEY_EVENT_MASK); if (isManagingFocus()) { LookAndFeel.installProperty(this, "focusTraversalKeysForward", getManagingFocusForwardTraversalKeys()); LookAndFeel.installProperty(this, "focusTraversalKeysBackward", getManagingFocusBackwardTraversalKeys()); } super.setLocale( JComponent.getDefaultLocale() ); } /** * Resets the UI property to a value from the current look and feel. * JComponent subclasses must override this method * like this: *

     *   public void updateUI() {
     *      setUI((SliderUI)UIManager.getUI(this);
     *   }
     *  
* * @see #setUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ public void updateUI() {} /** * Sets the look and feel delegate for this component. * JComponent subclasses generally override this method * to narrow the argument type. For example, in JSlider: *
     * public void setUI(SliderUI newUI) {
     *     super.setUI(newUI);
     * }
     *  
*

* Additionally JComponent subclasses must provide a * getUI method that returns the correct type. For example: *

     * public SliderUI getUI() {
     *     return (SliderUI)ui;
     * }
     * 
* * @param newUI the new UI delegate * @see #updateUI * @see UIManager#getLookAndFeel * @see UIManager#getUI * @beaninfo * bound: true * hidden: true * attribute: visualUpdate true * description: The component's look and feel delegate. */ protected void setUI(ComponentUI newUI) { /* We do not check that the UI instance is different * before allowing the switch in order to enable the * same UI instance *with different default settings* * to be installed. */ if (ui != null) { ui.uninstallUI(this); } // aaText shouldn't persist between look and feels, reset it. aaText = false; ComponentUI oldUI = ui; ui = newUI; if (ui != null) { ui.installUI(this); } firePropertyChange("UI", oldUI, newUI); revalidate(); repaint(); } /** * Returns the UIDefaults key used to * look up the name of the swing.plaf.ComponentUI * class that defines the look and feel * for this component. Most applications will never need to * call this method. Subclasses of JComponent that support * pluggable look and feel should override this method to * return a UIDefaults key that maps to the * ComponentUI subclass that defines their look and feel. * * @return the UIDefaults key for a * ComponentUI subclass * @see UIDefaults#getUI * @beaninfo * expert: true * description: UIClassID */ public String getUIClassID() { return uiClassID; } /** * Returns the graphics object used to paint this component. * If DebugGraphics is turned on we create a new * DebugGraphics object if necessary. * Otherwise we just configure the * specified graphics object's foreground and font. * * @param g the original Graphics object * @return a Graphics object configured for this component */ protected Graphics getComponentGraphics(Graphics g) { Graphics componentGraphics = g; if (ui != null && DEBUG_GRAPHICS_LOADED) { if ((DebugGraphics.debugComponentCount() != 0) && (shouldDebugGraphics() != 0) && !(g instanceof DebugGraphics)) { componentGraphics = new DebugGraphics(g,this); } } componentGraphics.setColor(getForeground()); componentGraphics.setFont(getFont()); return componentGraphics; } /** * Calls the UI delegate's paint method, if the UI delegate * is non-null. We pass the delegate a copy of the * Graphics object to protect the rest of the * paint code from irrevocable changes * (for example, Graphics.translate). *

* If you override this in a subclass you should not make permanent * changes to the passed in Graphics. For example, you * should not alter the clip Rectangle or modify the * transform. If you need to do these operations you may find it * easier to create a new Graphics from the passed in * Graphics and manipulate it. Further, if you do not * invoker super's implementation you must honor the opaque property, * that is * if this component is opaque, you must completely fill in the background * in a non-opaque color. If you do not honor the opaque property you * will likely see visual artifacts. *

* The passed in Graphics object might * have a transform other than the identify transform * installed on it. In this case, you might get * unexpected results if you cumulatively apply * another transform. * * @param g the Graphics object to protect * @see #paint * @see ComponentUI */ protected void paintComponent(Graphics g) { if (ui != null) { Graphics scratchGraphics = (g == null) ? null : g.create(); try { ui.update(scratchGraphics, this); } finally { scratchGraphics.dispose(); } } } /** * Paints this component's children. * If shouldUseBuffer is true, * no component ancestor has a buffer and * the component children can use a buffer if they have one. * Otherwise, one ancestor has a buffer currently in use and children * should not use a buffer to paint. * @param g the Graphics context in which to paint * @see #paint * @see java.awt.Container#paint */ protected void paintChildren(Graphics g) { boolean isJComponent; Graphics sg = null; try { synchronized(getTreeLock()) { int i = getComponentCount() - 1; if (i < 0) { return; } sg = (g == null) ? null : g.create(); // If we are only to paint to a specific child, determine // its index. if (paintingChild != null && (paintingChild instanceof JComponent) && ((JComponent)paintingChild).isOpaque()) { for (; i >= 0; i--) { if (getComponent(i) == paintingChild){ break; } } } Rectangle tmpRect = fetchRectangle(); boolean checkSiblings = (!isOptimizedDrawingEnabled() && checkIfChildObscuredBySibling()); Rectangle clipBounds = null; if (checkSiblings) { clipBounds = sg.getClipBounds(); if (clipBounds == null) { clipBounds = new Rectangle(0, 0, getWidth(), getHeight()); } } boolean printing = getFlag(IS_PRINTING); for (; i >= 0 ; i--) { Component comp = getComponent(i); if (comp != null && (printing || isLightweightComponent(comp)) && (comp.isVisible() == true)) { Rectangle cr; isJComponent = (comp instanceof JComponent); cr = comp.getBounds(tmpRect); boolean hitClip = g.hitClip(cr.x, cr.y, cr.width, cr.height); if (hitClip) { if (checkSiblings && i > 0) { int x = cr.x; int y = cr.y; int width = cr.width; int height = cr.height; SwingUtilities.computeIntersection (clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height, cr); if(getObscuredState(i, cr.x, cr.y, cr.width, cr.height) == COMPLETELY_OBSCURED) { continue; } cr.x = x; cr.y = y; cr.width = width; cr.height = height; } Graphics cg = sg.create(cr.x, cr.y, cr.width, cr.height); cg.setColor(comp.getForeground()); cg.setFont(comp.getFont()); boolean shouldSetFlagBack = false; try { if(isJComponent) { if(getFlag(ANCESTOR_USING_BUFFER)) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,true); shouldSetFlagBack = true; } if(getFlag(IS_PAINTING_TILE)) { ((JComponent)comp).setFlag(IS_PAINTING_TILE,true); shouldSetFlagBack = true; } if(!printing) { ((JComponent)comp).paint(cg); } else { if (!getFlag(IS_PRINTING_ALL)) { comp.print(cg); } else { comp.printAll(cg); } } } else { if (!printing) { comp.paint(cg); } else { if (!getFlag(IS_PRINTING_ALL)) { comp.print(cg); } else { comp.printAll(cg); } } } } finally { cg.dispose(); if(shouldSetFlagBack) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,false); ((JComponent)comp).setFlag(IS_PAINTING_TILE,false); } } } } } recycleRectangle(tmpRect); } } finally { if (sg != null) { sg.dispose(); } } } /** * Paints the component's border. *

* If you override this in a subclass you should not make permanent * changes to the passed in Graphics. For example, you * should not alter the clip Rectangle or modify the * transform. If you need to do these operations you may find it * easier to create a new Graphics from the passed in * Graphics and manipulate it. * * @param g the Graphics context in which to paint * * @see #paint * @see #setBorder */ protected void paintBorder(Graphics g) { Border border = getBorder(); if (border != null) { border.paintBorder(this, g, 0, 0, getWidth(), getHeight()); } } /** * Calls paint. Doesn't clear the background but see * ComponentUI.update, which is called by * paintComponent. * * @param g the Graphics context in which to paint * @see #paint * @see #paintComponent * @see javax.swing.plaf.ComponentUI */ public void update(Graphics g) { paint(g); } /** * Invoked by Swing to draw components. * Applications should not invoke paint directly, * but should instead use the repaint method to * schedule the component for redrawing. *

* This method actually delegates the work of painting to three * protected methods: paintComponent, * paintBorder, * and paintChildren. They're called in the order * listed to ensure that children appear on top of component itself. * Generally speaking, the component and its children should not * paint in the insets area allocated to the border. Subclasses can * just override this method, as always. A subclass that just * wants to specialize the UI (look and feel) delegate's * paint method should just override * paintComponent. * * @param g the Graphics context in which to paint * @see #paintComponent * @see #paintBorder * @see #paintChildren * @see #getComponentGraphics * @see #repaint */ public void paint(Graphics g) { boolean shouldClearPaintFlags = false; boolean paintCompleted = false; if ((getWidth() <= 0) || (getHeight() <= 0)) { return; } Graphics componentGraphics = getComponentGraphics(g); Graphics co = (componentGraphics == null) ? null : componentGraphics.create(); try { RepaintManager repaintManager = RepaintManager.currentManager(this); Rectangle clipRect = co.getClipBounds(); int clipX; int clipY; int clipW; int clipH; if (clipRect == null) { clipX = clipY = 0; clipW = getWidth(); clipH = getHeight(); } else { clipX = clipRect.x; clipY = clipRect.y; clipW = clipRect.width; clipH = clipRect.height; } if(clipW > getWidth()) { clipW = getWidth(); } if(clipH > getHeight()) { clipH = getHeight(); } if(getParent() != null && !(getParent() instanceof JComponent)) { adjustPaintFlags(); shouldClearPaintFlags = true; } int bw,bh; boolean printing = getFlag(IS_PRINTING); if(!printing && repaintManager.isDoubleBufferingEnabled() && !getFlag(ANCESTOR_USING_BUFFER) && isDoubleBuffered()) { paintCompleted = paintDoubleBuffered(this, this, co, clipX, clipY, clipW, clipH); } if (!paintCompleted) { // Will ocassionaly happen in 1.2, especially when printing. if (clipRect == null) { co.setClip(clipX, clipY, clipW, clipH); } if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) { if (!printing) { paintComponent(co); paintBorder(co); } else { printComponent(co); printBorder(co); } } if (!printing) { paintChildren(co); } else { printChildren(co); } } } finally { co.dispose(); if(shouldClearPaintFlags) { setFlag(ANCESTOR_USING_BUFFER,false); setFlag(IS_PAINTING_TILE,false); setFlag(IS_PRINTING,false); setFlag(IS_PRINTING_ALL,false); } } } /** * Returns true if this component, or any of it's ancestors, are in * the processing of painting. */ boolean isPainting() { Container component = this; while (component != null) { if (component instanceof JComponent && ((JComponent)component).getFlag(ANCESTOR_USING_BUFFER)) { return true; } component = component.getParent(); } return false; } private void adjustPaintFlags() { JComponent jparent = null; Container parent; for(parent = getParent() ; parent != null ; parent = parent.getParent()) { if(parent instanceof JComponent) { jparent = (JComponent) parent; if(jparent.getFlag(ANCESTOR_USING_BUFFER)) setFlag(ANCESTOR_USING_BUFFER, true); if(jparent.getFlag(IS_PAINTING_TILE)) setFlag(IS_PAINTING_TILE, true); if(jparent.getFlag(IS_PRINTING)) setFlag(IS_PRINTING, true); if(jparent.getFlag(IS_PRINTING_ALL)) setFlag(IS_PRINTING_ALL, true); break; } } } /** * Invoke this method to print the component. This method invokes * print on the component. * * @param g the Graphics context in which to paint * @see #print * @see #printComponent * @see #printBorder * @see #printChildren */ public void printAll(Graphics g) { setFlag(IS_PRINTING_ALL, true); try { print(g); } finally { setFlag(IS_PRINTING_ALL, false); } } /** * Invoke this method to print the component. This method will * result in invocations to printComponent, * printBorder and printChildren. It is * not recommended that you override this method, instead override * one of the previously mentioned methods. This method sets the * component's state such that the double buffer will not be used, eg * painting will be done directly on the passed in Graphics. * * @param g the Graphics context in which to paint * @see #printComponent * @see #printBorder * @see #printChildren */ public void print(Graphics g) { setFlag(IS_PRINTING, true); try { paint(g); } finally { setFlag(IS_PRINTING, false); } } /** * This is invoked during a printing operation. This is implemented to * invoke paintComponent on the component. Override this * if you wish to add special painting behavior when printing. * * @param g the Graphics context in which to paint * @see #print * @since 1.3 */ protected void printComponent(Graphics g) { paintComponent(g); } /** * Prints this component's children. This is implemented to invoke * paintChildren on the component. Override this if you * wish to print the children differently than painting. * * @param g the Graphics context in which to paint * @see #print * @since 1.3 */ protected void printChildren(Graphics g) { paintChildren(g); } /** * Prints the component's border. This is implemented to invoke * paintBorder on the component. Override this if you * wish to print the border differently that it is painted. * * @param g the Graphics context in which to paint * @see #print * @since 1.3 */ protected void printBorder(Graphics g) { paintBorder(g); } /** * Returns true if the component is currently painting a tile. * If this method returns true, paint will be called again for another * tile. This method returns false if you are not painting a tile or * if the last tile is painted. * Use this method to keep some state you might need between tiles. * * @return true if the component is currently painting a tile, * false otherwise */ public boolean isPaintingTile() { return getFlag(IS_PAINTING_TILE); } /** * In release 1.4, the focus subsystem was rearchitected. * For more information, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. *

* Changes this JComponent's focus traversal keys to * CTRL+TAB and CTRL+SHIFT+TAB. Also prevents * SortingFocusTraversalPolicy from considering descendants * of this JComponent when computing a focus traversal cycle. * * @see java.awt.Component#setFocusTraversalKeys * @see SortingFocusTraversalPolicy * @deprecated As of 1.4, replaced by * Component.setFocusTraversalKeys(int, Set) and * Container.setFocusCycleRoot(boolean). */ @Deprecated public boolean isManagingFocus() { return false; } private void registerNextFocusableComponent() { registerNextFocusableComponent(getNextFocusableComponent()); } private void registerNextFocusableComponent(Component nextFocusableComponent) { if (nextFocusableComponent == null) { return; } Container nearestRoot = (isFocusCycleRoot()) ? this : getFocusCycleRootAncestor(); FocusTraversalPolicy policy = nearestRoot.getFocusTraversalPolicy(); if (!(policy instanceof LegacyGlueFocusTraversalPolicy)) { policy = new LegacyGlueFocusTraversalPolicy(policy); nearestRoot.setFocusTraversalPolicy(policy); } ((LegacyGlueFocusTraversalPolicy)policy). setNextFocusableComponent(this, nextFocusableComponent); } private void deregisterNextFocusableComponent() { Component nextFocusableComponent = getNextFocusableComponent(); if (nextFocusableComponent == null) { return; } Container nearestRoot = (isFocusCycleRoot()) ? this : getFocusCycleRootAncestor(); if (nearestRoot == null) { return; } FocusTraversalPolicy policy = nearestRoot.getFocusTraversalPolicy(); if (policy instanceof LegacyGlueFocusTraversalPolicy) { ((LegacyGlueFocusTraversalPolicy)policy). unsetNextFocusableComponent(this, nextFocusableComponent); } } /** * In release 1.4, the focus subsystem was rearchitected. * For more information, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. *

* Overrides the default FocusTraversalPolicy for this * JComponent's focus traversal cycle by unconditionally * setting the specified Component as the next * Component in the cycle, and this JComponent * as the specified Component's previous * Component in the cycle. * * @param aComponent the Component that should follow this * JComponent in the focus traversal cycle * * @see #getNextFocusableComponent * @see java.awt.FocusTraversalPolicy * @deprecated As of 1.4, replaced by FocusTraversalPolicy */ @Deprecated public void setNextFocusableComponent(Component aComponent) { boolean displayable = isDisplayable(); if (displayable) { deregisterNextFocusableComponent(); } putClientProperty(NEXT_FOCUS, aComponent); if (displayable) { registerNextFocusableComponent(aComponent); } } /** * In release 1.4, the focus subsystem was rearchitected. * For more information, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. *

* Returns the Component set by a prior call to * setNextFocusableComponent(Component) on this * JComponent. * * @return the Component that will follow this * JComponent in the focus traversal cycle, or * null if none has been explicitly specified * * @see #setNextFocusableComponent * @deprecated As of 1.4, replaced by FocusTraversalPolicy. */ @Deprecated public Component getNextFocusableComponent() { return (Component)getClientProperty(NEXT_FOCUS); } /** * Provides a hint as to whether or not this JComponent * should get focus. This is only a hint, and it is up to consumers that * are requesting focus to honor this property. This is typically honored * for mouse operations, but not keyboard operations. For example, look * and feels could verify this property is true before requesting focus * during a mouse operation. This would often times be used if you did * not want a mouse press on a JComponent to steal focus, * but did want the JComponent to be traversable via the * keyboard. If you do not want this JComponent focusable at * all, use the setFocusable method instead. *

* Please see * * How to Use the Focus Subsystem, * a section in The Java Tutorial, * for more information. * * @param requestFocusEnabled indicates whether you want this * JComponent to be focusable or not * @see Focus Specification * @see java.awt.Component#setFocusable */ public void setRequestFocusEnabled(boolean requestFocusEnabled) { setFlag(REQUEST_FOCUS_DISABLED, !requestFocusEnabled); } /** * Returns true if this JComponent should * get focus; otherwise returns false. *

* Please see * * How to Use the Focus Subsystem, * a section in The Java Tutorial, * for more information. * * @return true if this component should get focus, * otherwise returns false * @see #setRequestFocusEnabled * @see Focus * Specification * @see java.awt.Component#isFocusable */ public boolean isRequestFocusEnabled() { return !getFlag(REQUEST_FOCUS_DISABLED); } private boolean runInputVerifier() { if (inInputVerifier) { // We're already running the InputVerifier, assume the // developer knows what they're doing. return true; } Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager(). getFocusOwner(); if (focusOwner == null) { // If we are moving focus from another window, we should detect // what element was in focus in the window that will be focused now. // To do this, static package private method // KeyboardFocusManager.getMostRecentFocusOwner() will be called. // We will use AccessController.doPrivileged() to make package // private method accessible. Window window = SwingUtilities.getWindowAncestor(this); if (window != null) { try { Method accessibleMethod = java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction() { public Method run() throws Exception { Method method = KeyboardFocusManager.class.getDeclaredMethod( "getMostRecentFocusOwner", Window.class); method.setAccessible(true); return method; } } ); focusOwner = (Component)(accessibleMethod.invoke(null, window)); } catch (Exception e) { focusOwner = null; } } } if (focusOwner == this) { return true; } if (!getVerifyInputWhenFocusTarget()) { return true; } if (focusOwner == null || !(focusOwner instanceof JComponent)) { return true; } JComponent jFocusOwner = (JComponent)focusOwner; InputVerifier iv = jFocusOwner.getInputVerifier(); if (iv == null) { return true; } else { inInputVerifier = true; try { return iv.shouldYieldFocus(jFocusOwner); } finally { inInputVerifier = false; } } } /** * Requests that this Component gets the input focus. * Refer to {@link java.awt.Component#requestFocus() * Component.requestFocus()} for a complete description of * this method. *

* Note that the use of this method is discouraged because * its behavior is platform dependent. Instead we recommend the * use of {@link #requestFocusInWindow() requestFocusInWindow()}. * If you would like more information on focus, see * * * @see java.awt.Component#requestFocusInWindow() * @see java.awt.Component#requestFocusInWindow(boolean) * @since 1.4 */ public void requestFocus() { if (runInputVerifier()) { super.requestFocus(); } } /** * Requests that this Component gets the input focus. * Refer to {@link java.awt.Component#requestFocus(boolean) * Component.requestFocus(boolean)} for a complete description of * this method. *

* Note that the use of this method is discouraged because * its behavior is platform dependent. Instead we recommend the * use of {@link #requestFocusInWindow(boolean) * requestFocusInWindow(boolean)}. * If you would like more information on focus, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. * * @param temporary boolean indicating if the focus change is temporary * @return false if the focus change request is guaranteed to * fail; true if it is likely to succeed * @see java.awt.Component#requestFocusInWindow() * @see java.awt.Component#requestFocusInWindow(boolean) * @since 1.4 */ public boolean requestFocus(boolean temporary) { return (runInputVerifier()) ? super.requestFocus(temporary) : false; } /** * Requests that this Component gets the input focus. * Refer to {@link java.awt.Component#requestFocusInWindow() * Component.requestFocusInWindow()} for a complete description of * this method. *

* If you would like more information on focus, see * * How to Use the Focus Subsystem, * a section in The Java Tutorial. * * @return false if the focus change request is guaranteed to * fail; true if it is likely to succeed * @see java.awt.Component#requestFocusInWindow() * @see java.awt.Component#requestFocusInWindow(boolean) * @since 1.4 */ public boolean requestFocusInWindow() { return (runInputVerifier()) ? super.requestFocusInWindow() : false; } /** * Requests that this Component gets the input focus. * Refer to {@link java.awt.Component#requestFocusInWindow(boolean) * Component.requestFocusInWindow(boolean)} for a complete description of * this method. *

* If you would like more information on focus, see * * * @param temporary boolean indicating if the focus change is temporary * @return false if the focus change request is guaranteed to * fail; true if it is likely to succeed * @see java.awt.Component#requestFocusInWindow() * @see java.awt.Component#requestFocusInWindow(boolean) * @since 1.4 */ protected boolean requestFocusInWindow(boolean temporary) { return (runInputVerifier()) ? super.requestFocusInWindow(temporary) : false; } /** * Requests that this Component get the input focus, and that this * Component's top-level ancestor become the focused Window. This component * must be displayable, visible, and focusable for the request to be * granted. *

* This method is intended for use by focus implementations. Client code * should not use this method; instead, it should use * requestFocusInWindow(). * * @see #requestFocusInWindow() */ public void grabFocus() { requestFocus(); } /** * Sets the value to indicate whether input verifier for the * current focus owner will be called before this component requests * focus. The default is true. Set to false on components such as a * Cancel button or a scrollbar, which should activate even if the * input in the current focus owner is not "passed" by the input * verifier for that component. * * @param verifyInputWhenFocusTarget value for the * verifyInputWhenFocusTarget property * @see InputVerifier * @see #setInputVerifier * @see #getInputVerifier * @see #getVerifyInputWhenFocusTarget * * @since 1.3 * @beaninfo * bound: true * description: Whether the Component verifies input before accepting * focus. */ public void setVerifyInputWhenFocusTarget(boolean verifyInputWhenFocusTarget) { boolean oldVerifyInputWhenFocusTarget = this.verifyInputWhenFocusTarget; this.verifyInputWhenFocusTarget = verifyInputWhenFocusTarget; firePropertyChange("verifyInputWhenFocusTarget", oldVerifyInputWhenFocusTarget, verifyInputWhenFocusTarget); } /** * Returns the value that indicates whether the input verifier for the * current focus owner will be called before this component requests * focus. * * @return value of the verifyInputWhenFocusTarget property * * @see InputVerifier * @see #setInputVerifier * @see #getInputVerifier * @see #setVerifyInputWhenFocusTarget * * @since 1.3 */ public boolean getVerifyInputWhenFocusTarget() { return verifyInputWhenFocusTarget; } /** * Gets the FontMetrics for the specified Font. * * @param font the font for which font metrics is to be * obtained * @return the font metrics for font * @throws NullPointerException if font is null * @since 1.5 */ public FontMetrics getFontMetrics(Font font) { if (font != null && SwingUtilities2.drawTextAntialiased(aaText)) { synchronized(aaFontMap) { FontMetrics aaMetrics = aaFontMap.get(font); if (aaMetrics == null) { aaMetrics = new FontDesignMetrics( font, SwingUtilities2.AA_FRC); aaFontMap.put(font, aaMetrics); } return aaMetrics; } } return super.getFontMetrics(font); } /** * Sets the preferred size of this component. * If preferredSize is null, the UI will * be asked for the preferred size. * @beaninfo * preferred: true * bound: true * description: The preferred size of the component. */ public void setPreferredSize(Dimension preferredSize) { super.setPreferredSize(preferredSize); } /** * If the preferredSize has been set to a * non-null value just returns it. * If the UI delegate's getPreferredSize * method returns a non null value then return that; * otherwise defer to the component's layout manager. * * @return the value of the preferredSize property * @see #setPreferredSize * @see ComponentUI */ public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } Dimension size = null; if (ui != null) { size = ui.getPreferredSize(this); } return (size != null) ? size : super.getPreferredSize(); } /** * Sets the maximum size of this component to a constant * value. Subsequent calls to getMaximumSize will always * return this value; the component's UI will not be asked * to compute it. Setting the maximum size to null * restores the default behavior. * * @param maximumSize a Dimension containing the * desired maximum allowable size * @see #getMaximumSize * @beaninfo * bound: true * description: The maximum size of the component. */ public void setMaximumSize(Dimension maximumSize) { super.setMaximumSize(maximumSize); } /** * If the maximum size has been set to a non-null value * just returns it. If the UI delegate's getMaximumSize * method returns a non-null value then return that; * otherwise defer to the component's layout manager. * * @return the value of the maximumSize property * @see #setMaximumSize * @see ComponentUI */ public Dimension getMaximumSize() { if (isMaximumSizeSet()) { return super.getMaximumSize(); } Dimension size = null; if (ui != null) { size = ui.getMaximumSize(this); } return (size != null) ? size : super.getMaximumSize(); } /** * Sets the minimum size of this component to a constant * value. Subsequent calls to getMinimumSize will always * return this value; the component's UI will not be asked * to compute it. Setting the minimum size to null * restores the default behavior. * * @param minimumSize the new minimum size of this component * @see #getMinimumSize * @beaninfo * bound: true * description: The minimum size of the component. */ public void setMinimumSize(Dimension minimumSize) { super.setMinimumSize(minimumSize); } /** * If the minimum size has been set to a non-null value * just returns it. If the UI delegate's getMinimumSize * method returns a non-null value then return that; otherwise * defer to the component's layout manager. * * @return the value of the minimumSize property * @see #setMinimumSize * @see ComponentUI */ public Dimension getMinimumSize() { if (isMinimumSizeSet()) { return super.getMinimumSize(); } Dimension size = null; if (ui != null) { size = ui.getMinimumSize(this); } return (size != null) ? size : super.getMinimumSize(); } /** * Gives the UI delegate an opportunity to define the precise * shape of this component for the sake of mouse processing. * * @return true if this component logically contains x,y * @see java.awt.Component#contains(int, int) * @see ComponentUI */ public boolean contains(int x, int y) { return (ui != null) ? ui.contains(this, x, y) : super.contains(x, y); } /** * Sets the border of this component. The Border object is * responsible for defining the insets for the component * (overriding any insets set directly on the component) and * for optionally rendering any border decorations within the * bounds of those insets. Borders should be used (rather * than insets) for creating both decorative and non-decorative * (such as margins and padding) regions for a swing component. * Compound borders can be used to nest multiple borders within a * single component. *

* Although technically you can set the border on any object * that inherits from JComponent, the look and * feel implementation of many standard Swing components * doesn't work well with user-set borders. In general, * when you want to set a border on a standard Swing * component other than JPanel or JLabel, * we recommend that you put the component in a JPanel * and set the border on the JPanel. *

* This is a bound property. * * @param border the border to be rendered for this component * @see Border * @see CompoundBorder * @beaninfo * bound: true * preferred: true * attribute: visualUpdate true * description: The component's border. */ public void setBorder(Border border) { Border oldBorder = this.border; this.border = border; firePropertyChange("border", oldBorder, border); if (border != oldBorder) { if (border == null || oldBorder == null || !(border.getBorderInsets(this).equals(oldBorder.getBorderInsets(this)))) { revalidate(); } repaint(); } } /** * Returns the border of this component or null if no * border is currently set. * * @return the border object for this component * @see #setBorder */ public Border getBorder() { return border; } /** * If a border has been set on this component, returns the * border's insets; otherwise calls super.getInsets. * * @return the value of the insets property * @see #setBorder */ public Insets getInsets() { if (border != null) { return border.getBorderInsets(this); } return super.getInsets(); } /** * Returns an Insets object containing this component's inset * values. The passed-in Insets object will be reused * if possible. * Calling methods cannot assume that the same object will be returned, * however. All existing values within this object are overwritten. * If insets is null, this will allocate a new one. * * @param insets the Insets object, which can be reused * @return the Insets object * @see #getInsets * @beaninfo * expert: true */ public Insets getInsets(Insets insets) { if (insets == null) { insets = new Insets(0, 0, 0, 0); } if (border != null) { if (border instanceof AbstractBorder) { return ((AbstractBorder)border).getBorderInsets(this, insets); } else { // Can't reuse border insets because the Border interface // can't be enhanced. return border.getBorderInsets(this); } } else { // super.getInsets() always returns an Insets object with // all of its value zeroed. No need for a new object here. insets.left = insets.top = insets.right = insets.bottom = 0; return insets; } } /** * Overrides Container.getAlignmentY to return * the horizontal alignment. * * @return the value of the alignmentY property * @see #setAlignmentY * @see java.awt.Component#getAlignmentY */ public float getAlignmentY() { if (isAlignmentYSet) { return alignmentY; } return super.getAlignmentY(); } /** * Sets the the horizontal alignment. * * @param alignmentY the new horizontal alignment * @see #getAlignmentY * @beaninfo * description: The preferred vertical alignment of the component. */ public void setAlignmentY(float alignmentY) { this.alignmentY = alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f : alignmentY; isAlignmentYSet = true; } /** * Overrides Container.getAlignmentX to return * the vertical alignment. * * @return the value of the alignmentX property * @see #setAlignmentX * @see java.awt.Component#getAlignmentX */ public float getAlignmentX() { if (isAlignmentXSet) { return alignmentX; } return super.getAlignmentX(); } /** * Sets the the vertical alignment. * * @param alignmentX the new vertical alignment * @see #getAlignmentX * @beaninfo * description: The preferred horizontal alignment of the component. */ public void setAlignmentX(float alignmentX) { this.alignmentX = alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX; isAlignmentXSet = true; } /** * Sets the input verifier for this component. * * @param inputVerifier the new input verifier * @since 1.3 * @see InputVerifier * @beaninfo * bound: true * description: The component's input verifier. */ public void setInputVerifier(InputVerifier inputVerifier) { InputVerifier oldInputVerifier = (InputVerifier)getClientProperty( INPUT_VERIFIER_KEY); putClientProperty(INPUT_VERIFIER_KEY, inputVerifier); firePropertyChange("inputVerifier", oldInputVerifier, inputVerifier); } /** * Returns the input verifier for this component. * * @return the inputVerifier property * @since 1.3 * @see InputVerifier */ public InputVerifier getInputVerifier() { return (InputVerifier)getClientProperty(INPUT_VERIFIER_KEY); } /** * Returns this component's graphics context, which lets you draw * on a component. Use this method get a Graphics object and * then invoke operations on that object to draw on the component. * @return this components graphics context */ public Graphics getGraphics() { if (DEBUG_GRAPHICS_LOADED && shouldDebugGraphics() != 0) { DebugGraphics graphics = new DebugGraphics(super.getGraphics(), this); return graphics; } return super.getGraphics(); } /** Enables or disables diagnostic information about every graphics * operation performed within the component or one of its children. * * @param debugOptions determines how the component should display * the information; one of the following options: *

* debugOptions is bitwise OR'd into the current value * * @beaninfo * preferred: true * enum: NONE_OPTION DebugGraphics.NONE_OPTION * LOG_OPTION DebugGraphics.LOG_OPTION * FLASH_OPTION DebugGraphics.FLASH_OPTION * BUFFERED_OPTION DebugGraphics.BUFFERED_OPTION * description: Diagnostic options for graphics operations. */ public void setDebugGraphicsOptions(int debugOptions) { DebugGraphics.setDebugOptions(this, debugOptions); } /** Returns the state of graphics debugging. * * @return a bitwise OR'd flag of zero or more of the following options: *
    *
  • DebugGraphics.LOG_OPTION - causes a text message to be printed. *
  • DebugGraphics.FLASH_OPTION - causes the drawing to flash several * times. *
  • DebugGraphics.BUFFERED_OPTION - creates an * ExternalWindow that displays the operations * performed on the View's offscreen buffer. *
  • DebugGraphics.NONE_OPTION disables debugging. *
  • A value of 0 causes no changes to the debugging options. *
* @see #setDebugGraphicsOptions */ public int getDebugGraphicsOptions() { return DebugGraphics.getDebugOptions(this); } /** * Returns true if debug information is enabled for this * JComponent or one of its parents. */ int shouldDebugGraphics() { return DebugGraphics.shouldComponentDebug(this); } /** * This method is now obsolete, please use a combination of * getActionMap() and getInputMap() for * similiar behavior. For example, to bind the KeyStroke * aKeyStroke to the Action anAction * now use: *
     *   component.getInputMap().put(aKeyStroke, aCommand);
     *   component.getActionMap().put(aCommmand, anAction);
     * 
* The above assumes you want the binding to be applicable for * WHEN_FOCUSED. To register bindings for other focus * states use the getInputMap method that takes an integer. *

* Register a new keyboard action. * anAction will be invoked if a key event matching * aKeyStroke occurs and aCondition is verified. * The KeyStroke object defines a * particular combination of a keyboard key and one or more modifiers * (alt, shift, ctrl, meta). *

* The aCommand will be set in the delivered event if * specified. *

* The aCondition can be one of: *

*
*
WHEN_FOCUSED *
The action will be invoked only when the keystroke occurs * while the component has the focus. *
WHEN_IN_FOCUSED_WINDOW *
The action will be invoked when the keystroke occurs while * the component has the focus or if the component is in the * window that has the focus. Note that the component need not * be an immediate descendent of the window -- it can be * anywhere in the window's containment hierarchy. In other * words, whenever any component in the window has the focus, * the action registered with this component is invoked. *
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
The action will be invoked when the keystroke occurs while the * component has the focus or if the component is an ancestor of * the component that has the focus. *
*
*

* The combination of keystrokes and conditions lets you define high * level (semantic) action events for a specified keystroke+modifier * combination (using the KeyStroke class) and direct to a parent or * child of a component that has the focus, or to the component itself. * In other words, in any hierarchical structure of components, an * arbitrary key-combination can be immediately directed to the * appropriate component in the hierarchy, and cause a specific method * to be invoked (usually by way of adapter objects). *

* If an action has already been registered for the receiving * container, with the same charCode and the same modifiers, * anAction will replace the action. * * @param anAction the Action to be registered * @param aCommand the command to be set in the delivered event * @param aKeyStroke the KeyStroke to bind to the action * @param aCondition the condition that needs to be met, see above * @see KeyStroke */ public void registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition) { InputMap inputMap = getInputMap(aCondition, true); if (inputMap != null) { ActionMap actionMap = getActionMap(true); ActionStandin action = new ActionStandin(anAction, aCommand); inputMap.put(aKeyStroke, action); if (actionMap != null) { actionMap.put(action, action); } } } /** * Registers any bound WHEN_IN_FOCUSED_WINDOW actions with * the KeyboardManager. If onlyIfNew * is true only actions that haven't been registered are pushed * to the KeyboardManager; * otherwise all actions are pushed to the KeyboardManager. * * @param onlyIfNew if true, only actions that haven't been registered * are pushed to the KeyboardManager */ private void registerWithKeyboardManager(boolean onlyIfNew) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW, false); KeyStroke[] strokes; Hashtable registered = (Hashtable)getClientProperty (WHEN_IN_FOCUSED_WINDOW_BINDINGS); if (inputMap != null) { // Push any new KeyStrokes to the KeyboardManager. strokes = inputMap.allKeys(); if (strokes != null) { for (int counter = strokes.length - 1; counter >= 0; counter--) { if (!onlyIfNew || registered == null || registered.get(strokes[counter]) == null) { registerWithKeyboardManager(strokes[counter]); } if (registered != null) { registered.remove(strokes[counter]); } } } } else { strokes = null; } // Remove any old ones. if (registered != null && registered.size() > 0) { Enumeration keys = registered.keys(); while (keys.hasMoreElements()) { KeyStroke ks = (KeyStroke)keys.nextElement(); unregisterWithKeyboardManager(ks); } registered.clear(); } // Updated the registered Hashtable. if (strokes != null && strokes.length > 0) { if (registered == null) { registered = new Hashtable(strokes.length); putClientProperty(WHEN_IN_FOCUSED_WINDOW_BINDINGS, registered); } for (int counter = strokes.length - 1; counter >= 0; counter--) { registered.put(strokes[counter], strokes[counter]); } } else { putClientProperty(WHEN_IN_FOCUSED_WINDOW_BINDINGS, null); } } /** * Unregisters all the previously registered * WHEN_IN_FOCUSED_WINDOW KeyStroke bindings. */ private void unregisterWithKeyboardManager() { Hashtable registered = (Hashtable)getClientProperty (WHEN_IN_FOCUSED_WINDOW_BINDINGS); if (registered != null && registered.size() > 0) { Enumeration keys = registered.keys(); while (keys.hasMoreElements()) { KeyStroke ks = (KeyStroke)keys.nextElement(); unregisterWithKeyboardManager(ks); } } putClientProperty(WHEN_IN_FOCUSED_WINDOW_BINDINGS, null); } /** * Invoked from ComponentInputMap when its bindings change. * If inputMap is the current windowInputMap * (or a parent of the window InputMap) * the KeyboardManager is notified of the new bindings. * * @param inputMap the map containing the new bindings */ void componentInputMapChanged(ComponentInputMap inputMap) { InputMap km = getInputMap(WHEN_IN_FOCUSED_WINDOW, false); while (km != inputMap && km != null) { km = (ComponentInputMap)km.getParent(); } if (km != null) { registerWithKeyboardManager(false); } } private void registerWithKeyboardManager(KeyStroke aKeyStroke) { KeyboardManager.getCurrentManager().registerKeyStroke(aKeyStroke,this); } private void unregisterWithKeyboardManager(KeyStroke aKeyStroke) { KeyboardManager.getCurrentManager().unregisterKeyStroke(aKeyStroke, this); } /** * This method is now obsolete, please use a combination of * getActionMap() and getInputMap() for * similiar behavior. */ public void registerKeyboardAction(ActionListener anAction,KeyStroke aKeyStroke,int aCondition) { registerKeyboardAction(anAction,null,aKeyStroke,aCondition); } /** * This method is now obsolete. To unregister an existing binding * you can either remove the binding from the * ActionMap/InputMap, or place a dummy binding the * InputMap. Removing the binding from the * InputMap allows bindings in parent InputMaps * to be active, whereas putting a dummy binding in the * InputMap effectively disables * the binding from ever happening. *

* Unregisters a keyboard action. * This will remove the binding from the ActionMap * (if it exists) as well as the InputMaps. */ public void unregisterKeyboardAction(KeyStroke aKeyStroke) { ActionMap am = getActionMap(false); for (int counter = 0; counter < 3; counter++) { InputMap km = getInputMap(counter, false); if (km != null) { Object actionID = km.get(aKeyStroke); if (am != null && actionID != null) { am.remove(actionID); } km.remove(aKeyStroke); } } } /** * Returns the KeyStrokes that will initiate * registered actions. * * @return an array of KeyStroke objects * @see #registerKeyboardAction */ public KeyStroke[] getRegisteredKeyStrokes() { int[] counts = new int[3]; KeyStroke[][] strokes = new KeyStroke[3][]; for (int counter = 0; counter < 3; counter++) { InputMap km = getInputMap(counter, false); strokes[counter] = (km != null) ? km.allKeys() : null; counts[counter] = (strokes[counter] != null) ? strokes[counter].length : 0; } KeyStroke[] retValue = new KeyStroke[counts[0] + counts[1] + counts[2]]; for (int counter = 0, last = 0; counter < 3; counter++) { if (counts[counter] > 0) { System.arraycopy(strokes[counter], 0, retValue, last, counts[counter]); last += counts[counter]; } } return retValue; } /** * Returns the condition that determines whether a registered action * occurs in response to the specified keystroke. *

* For Java 2 platform v1.3, a KeyStroke can be associated * with more than one condition. * For example, 'a' could be bound for the two * conditions WHEN_FOCUSED and * WHEN_IN_FOCUSED_WINDOW condition. * * @return the action-keystroke condition */ public int getConditionForKeyStroke(KeyStroke aKeyStroke) { for (int counter = 0; counter < 3; counter++) { InputMap inputMap = getInputMap(counter, false); if (inputMap != null && inputMap.get(aKeyStroke) != null) { return counter; } } return UNDEFINED_CONDITION; } /** * Returns the object that will perform the action registered for a * given keystroke. * * @return the ActionListener * object invoked when the keystroke occurs */ public ActionListener getActionForKeyStroke(KeyStroke aKeyStroke) { ActionMap am = getActionMap(false); if (am == null) { return null; } for (int counter = 0; counter < 3; counter++) { InputMap inputMap = getInputMap(counter, false); if (inputMap != null) { Object actionBinding = inputMap.get(aKeyStroke); if (actionBinding != null) { Action action = am.get(actionBinding); if (action instanceof ActionStandin) { return ((ActionStandin)action).actionListener; } return action; } } } return null; } /** * Unregisters all the bindings in the first tier InputMaps * and ActionMap. This has the effect of removing any * local bindings, and allowing the bindings defined in parent * InputMap/ActionMaps * (the UI is usually defined in the second tier) to persist. */ public void resetKeyboardActions() { // Keys for (int counter = 0; counter < 3; counter++) { InputMap inputMap = getInputMap(counter, false); if (inputMap != null) { inputMap.clear(); } } // Actions ActionMap am = getActionMap(false); if (am != null) { am.clear(); } } /** * Sets the InputMap to use under the condition * condition to * map. A null value implies you * do not want any bindings to be used, even from the UI. This will * not reinstall the UI InputMap (if there was one). * condition has one of the following values: *

    *
  • WHEN_IN_FOCUSED_WINDOW *
  • WHEN_FOCUSED *
  • WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
* If condition is WHEN_IN_FOCUSED_WINDOW * and map is not a ComponentInputMap, an * IllegalArgumentException will be thrown. * Similarly, if condition is not one of the values * listed, an IllegalArgumentException will be thrown. * * @param condition one of the values listed above * @param map the InputMap to use for the given condition * @exception IllegalArgumentException if condition is * WHEN_IN_FOCUSED_WINDOW and map * is not an instance of ComponentInputMap; or * if condition is not one of the legal values * specified above * @since 1.3 */ public final void setInputMap(int condition, InputMap map) { switch (condition) { case WHEN_IN_FOCUSED_WINDOW: if (map != null && !(map instanceof ComponentInputMap)) { throw new IllegalArgumentException("WHEN_IN_FOCUSED_WINDOW InputMaps must be of type ComponentInputMap"); } windowInputMap = (ComponentInputMap)map; setFlag(WIF_INPUTMAP_CREATED, true); registerWithKeyboardManager(false); break; case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: ancestorInputMap = map; setFlag(ANCESTOR_INPUTMAP_CREATED, true); break; case WHEN_FOCUSED: focusInputMap = map; setFlag(FOCUS_INPUTMAP_CREATED, true); break; default: throw new IllegalArgumentException("condition must be one of JComponent.WHEN_IN_FOCUSED_WINDOW, JComponent.WHEN_FOCUSED or JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT"); } } /** * Returns the InputMap that is used during * condition. * * @param condition one of WHEN_IN_FOCUSED_WINDOW, WHEN_FOCUSED, * WHEN_ANCESTOR_OF_FOCUSED_COMPONENT * @return the InputMap for the specified * condition * @since 1.3 */ public final InputMap getInputMap(int condition) { return getInputMap(condition, true); } /** * Returns the InputMap that is used when the * component has focus. * This is convenience method for getInputMap(WHEN_FOCUSED). * * @return the InputMap used when the component has focus * @since JDK1.3 */ public final InputMap getInputMap() { return getInputMap(WHEN_FOCUSED, true); } /** * Sets the ActionMap to am. This does not set * the parent of the am to be the ActionMap * from the UI (if there was one), it is up to the caller to have done this. * * @param am the new ActionMap * @since 1.3 */ public final void setActionMap(ActionMap am) { actionMap = am; setFlag(ACTIONMAP_CREATED, true); } /** * Returns the ActionMap used to determine what * Action to fire for particular KeyStroke * binding. The returned ActionMap, unless otherwise * set, will have the ActionMap from the UI set as the parent. * * @return the ActionMap containing the key/action bindings * @since 1.3 */ public final ActionMap getActionMap() { return getActionMap(true); } /** * Returns the InputMap to use for condition * condition. If the InputMap hasn't * been created, and create is * true, it will be created. * * @param condition one of the following values: *
    *
  • JComponent.FOCUS_INPUTMAP_CREATED *
  • JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
  • JComponent.WHEN_IN_FOCUSED_WINDOW *
* @param create if true, create the InputMap if it * is not already created * @return the InputMap for the given condition; * if create is false and the InputMap * hasn't been created, returns null * @exception IllegalArgumentException if condition * is not one of the legal values listed above */ final InputMap getInputMap(int condition, boolean create) { switch (condition) { case WHEN_FOCUSED: if (getFlag(FOCUS_INPUTMAP_CREATED)) { return focusInputMap; } // Hasn't been created yet. if (create) { InputMap km = new InputMap(); setInputMap(condition, km); return km; } break; case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: if (getFlag(ANCESTOR_INPUTMAP_CREATED)) { return ancestorInputMap; } // Hasn't been created yet. if (create) { InputMap km = new InputMap(); setInputMap(condition, km); return km; } break; case WHEN_IN_FOCUSED_WINDOW: if (getFlag(WIF_INPUTMAP_CREATED)) { return windowInputMap; } // Hasn't been created yet. if (create) { ComponentInputMap km = new ComponentInputMap(this); setInputMap(condition, km); return km; } break; default: throw new IllegalArgumentException("condition must be one of JComponent.WHEN_IN_FOCUSED_WINDOW, JComponent.WHEN_FOCUSED or JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT"); } return null; } /** * Finds and returns the appropriate ActionMap. * * @param create if true, create the ActionMap if it * is not already created * @return the ActionMap for this component; if the * create flag is false and there is no * current ActionMap, returns null */ final ActionMap getActionMap(boolean create) { if (getFlag(ACTIONMAP_CREATED)) { return actionMap; } // Hasn't been created. if (create) { ActionMap am = new ActionMap(); setActionMap(am); return am; } return null; } /** * In release 1.4, the focus subsystem was rearchitected. * For more information, see *
* How to Use the Focus Subsystem, * a section in The Java Tutorial. *

* Requests focus on this JComponent's * FocusTraversalPolicy's default Component. * If this JComponent is a focus cycle root, then its * FocusTraversalPolicy is used. Otherwise, the * FocusTraversalPolicy of this JComponent's * focus-cycle-root ancestor is used. * * @see java.awt.FocusTraversalPolicy#getDefaultComponent * @deprecated As of 1.4, replaced by * FocusTraversalPolicy.getDefaultComponent(Container).requestFocus() */ @Deprecated public boolean requestDefaultFocus() { Container nearestRoot = (isFocusCycleRoot()) ? this : getFocusCycleRootAncestor(); if (nearestRoot == null) { return false; } Component comp = nearestRoot.getFocusTraversalPolicy(). getDefaultComponent(nearestRoot); if (comp != null) { comp.requestFocus(); return true; } else { return false; } } /** * Makes the component visible or invisible. * Overrides Component.setVisible. * * @param aFlag true to make the component visible; false to * make it invisible * * @beaninfo * attribute: visualUpdate true */ public void setVisible(boolean aFlag) { if(aFlag != isVisible()) { super.setVisible(aFlag); Container parent = getParent(); if(parent != null) { Rectangle r = getBounds(); parent.repaint(r.x,r.y,r.width,r.height); } // Some (all should) LayoutManagers do not consider components // that are not visible. As such we need to revalidate when the // visible bit changes. revalidate(); } } /** * Sets whether or not this component is enabled. * A component that is enabled may respond to user input, * while a component that is not enabled cannot respond to * user input. Some components may alter their visual * representation when they are disabled in order to * provide feedback to the user that they cannot take input. *

Note: Disabling a component does not disable it's children. * *

Note: Disabling a lightweight component does not prevent it from * receiving MouseEvents. * * @param enabled true if this component should be enabled, false otherwise * @see java.awt.Component#isEnabled * @see java.awt.Component#isLightweight * * @beaninfo * preferred: true * bound: true * attribute: visualUpdate true * description: The enabled state of the component. */ public void setEnabled(boolean enabled) { boolean oldEnabled = isEnabled(); super.setEnabled(enabled); firePropertyChange("enabled", oldEnabled, enabled); if (enabled != oldEnabled) { repaint(); } } /** * Sets the foreground color of this component. * * @param fg the desired foreground Color * @see java.awt.Component#getForeground * * @beaninfo * preferred: true * bound: true * attribute: visualUpdate true * description: The foreground color of the component. */ public void setForeground(Color fg) { Color oldFg = getForeground(); super.setForeground(fg); if ((oldFg != null) ? !oldFg.equals(fg) : ((fg != null) && !fg.equals(oldFg))) { // foreground already bound in AWT1.2 repaint(); } } /** * Sets the background color of this component. * * @param bg the desired background Color * @see java.awt.Component#getBackground * * @beaninfo * preferred: true * bound: true * attribute: visualUpdate true * description: The background color of the component. */ public void setBackground(Color bg) { Color oldBg = getBackground(); super.setBackground(bg); if ((oldBg != null) ? !oldBg.equals(bg) : ((bg != null) && !bg.equals(oldBg))) { // background already bound in AWT1.2 repaint(); } } /** * Sets the font for this component. * * @param font the desired Font for this component * @see java.awt.Component#getFont * * @beaninfo * preferred: true * bound: true * attribute: visualUpdate true * description: The font for the component. */ public void setFont(Font font) { Font oldFont = getFont(); super.setFont(font); // font already bound in AWT1.2 if (font != oldFont) { revalidate(); repaint(); } } /** * Returns the default locale used to initialize each JComponent's * locale property upon creation. * * The default locale has "AppContext" scope so that applets (and * potentially multiple lightweight applications running in a single VM) * can have their own setting. An applet can safely alter its default * locale because it will have no affect on other applets (or the browser). * * @return the default Locale. * @see #setDefaultLocale * @see java.awt.Component#getLocale * @see #setLocale * @since 1.4 */ static public Locale getDefaultLocale() { Locale l = (Locale) SwingUtilities.appContextGet(defaultLocale); if( l == null ) { //REMIND(bcb) choosing the default value is more complicated //than this. l = Locale.getDefault(); JComponent.setDefaultLocale( l ); } return l; } /** * Sets the default locale used to initialize each JComponent's locale * property upon creation. The initial value is the VM's default locale. * * The default locale has "AppContext" scope so that applets (and * potentially multiple lightweight applications running in a single VM) * can have their own setting. An applet can safely alter its default * locale because it will have no affect on other applets (or the browser). * * @param l the desired default Locale for new components. * @see #getDefaultLocale * @see java.awt.Component#getLocale * @see #setLocale * @since 1.4 */ static public void setDefaultLocale( Locale l ) { SwingUtilities.appContextPut(defaultLocale, l); } /** * Processes any key events that the component itself * recognizes. This is called after the focus * manager and any interested listeners have been * given a chance to steal away the event. This * method is called only if the event has not * yet been consumed. This method is called prior * to the keyboard UI logic. *

* This method is implemented to do nothing. Subclasses would * normally override this method if they process some * key events themselves. If the event is processed, * it should be consumed. */ protected void processComponentKeyEvent(KeyEvent e) { } /** Overrides processKeyEvent to process events. **/ protected void processKeyEvent(KeyEvent e) { boolean result; boolean shouldProcessKey; // This gives the key event listeners a crack at the event super.processKeyEvent(e); // give the component itself a crack at the event if (! e.isConsumed()) { processComponentKeyEvent(e); } shouldProcessKey = KeyboardState.shouldProcess(e); if(e.isConsumed()) { return; } if (shouldProcessKey && processKeyBindings(e, e.getID() == KeyEvent.KEY_PRESSED)) { e.consume(); } } /** * Invoked to process the key bindings for ks as the result * of the KeyEvent e. This obtains * the appropriate InputMap, * gets the binding, gets the action from the ActionMap, * and then (if the action is found and the component * is enabled) invokes notifyAction to notify the action. * * @param ks the KeyStroke queried * @param e the KeyEvent * @param condition one of the following values: *

    *
  • JComponent.WHEN_FOCUSED *
  • JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
  • JComponent.WHEN_IN_FOCUSED_WINDOW *
* @param pressed true if the key is pressed * @return true if there was a binding to an action, and the action * was enabled * * @since 1.3 */ protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { InputMap map = getInputMap(condition, false); ActionMap am = getActionMap(false); if(map != null && am != null && isEnabled()) { Object binding = map.get(ks); Action action = (binding == null) ? null : am.get(binding); if (action != null) { return SwingUtilities.notifyAction(action, ks, e, this, e.getModifiers()); } } return false; } /** * This is invoked as the result of a KeyEvent * that was not consumed by the FocusManager, * KeyListeners, or the component. It will first try * WHEN_FOCUSED bindings, * then WHEN_ANCESTOR_OF_FOCUSED_COMPONENT bindings, * and finally WHEN_IN_FOCUSED_WINDOW bindings. * * @param e the unconsumed KeyEvent * @param pressed true if the key is pressed * @return true if there is a key binding for e */ boolean processKeyBindings(KeyEvent e, boolean pressed) { if (!SwingUtilities.isValidKeyEventForKeyBindings(e)) { return false; } // Get the KeyStroke KeyStroke ks; if (e.getID() == KeyEvent.KEY_TYPED) { ks = KeyStroke.getKeyStroke(e.getKeyChar()); } else { ks = KeyStroke.getKeyStroke(e.getKeyCode(),e.getModifiers(), (pressed ? false:true)); } /* Do we have a key binding for e? */ if(processKeyBinding(ks, e, WHEN_FOCUSED, pressed)) return true; /* We have no key binding. Let's try the path from our parent to the * window excluded. We store the path components so we can avoid * asking the same component twice. */ Container parent = this; while (parent != null && !(parent instanceof Window) && !(parent instanceof Applet)) { if(parent instanceof JComponent) { if(((JComponent)parent).processKeyBinding(ks, e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed)) return true; } // This is done so that the children of a JInternalFrame are // given precedence for WHEN_IN_FOCUSED_WINDOW bindings before // other components WHEN_IN_FOCUSED_WINDOW bindings. This also gives // more precedence to the WHEN_IN_FOCUSED_WINDOW bindings of the // JInternalFrame's children vs the // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT bindings of the parents. // maybe generalize from JInternalFrame (like isFocusCycleRoot). if ((parent instanceof JInternalFrame) && JComponent.processKeyBindingsForAllComponents(e,parent,pressed)){ return true; } parent = parent.getParent(); } /* No components between the focused component and the window is * actually interested by the key event. Let's try the other * JComponent in this window. */ if(parent != null) { return JComponent.processKeyBindingsForAllComponents(e,parent,pressed); } return false; } static boolean processKeyBindingsForAllComponents(KeyEvent e, Container container, boolean pressed) { while (true) { if (KeyboardManager.getCurrentManager().fireKeyboardAction( e, pressed, container)) { return true; } if (container instanceof Popup.HeavyWeightWindow) { container = ((Window)container).getOwner(); } else { return false; } } } /** * Registers the text to display in a tool tip. * The text displays when the cursor lingers over the component. *

* See How to Use Tool Tips * in The Java Tutorial * for further documentation. * * @param text the string to display; if the text is null, * the tool tip is turned off for this component * @see #TOOL_TIP_TEXT_KEY * @beaninfo * preferred: true * description: The text to display in a tool tip. */ public void setToolTipText(String text) { String oldText = getToolTipText(); putClientProperty(TOOL_TIP_TEXT_KEY, text); ToolTipManager toolTipManager = ToolTipManager.sharedInstance(); if (text != null) { if (oldText == null) { toolTipManager.registerComponent(this); } } else { toolTipManager.unregisterComponent(this); } } /** * Returns the tooltip string that has been set with * setToolTipText. * * @return the text of the tool tip * @see #TOOL_TIP_TEXT_KEY */ public String getToolTipText() { return (String)getClientProperty(TOOL_TIP_TEXT_KEY); } /** * Returns the string to be used as the tooltip for event. * By default this returns any string set using * setToolTipText. If a component provides * more extensive API to support differing tooltips at different locations, * this method should be overridden. */ public String getToolTipText(MouseEvent event) { return getToolTipText(); } /** * Returns the tooltip location in this component's coordinate system. * If null is returned, Swing will choose a location. * The default implementation returns null. * * @param event the MouseEvent that caused the * ToolTipManager to show the tooltip * @return always returns null */ public Point getToolTipLocation(MouseEvent event) { return null; } /** * Returns the preferred location to display the popup menu in this * component's coordinate system. It is up to the look and feel to * honor this propery, some may choose to ignore it. If null * is truend the look and feel will choose a suitable location. * * @param event the MouseEvent that triggered the popup * to be shown, or null if popup was is not being shown as the * result of a mouse event * @return Locatino to display the JPopupMenu. * @since 1.5 */ public Point getPopupLocation(MouseEvent event) { return null; } /** * Returns the instance of JToolTip that should be used * to display the tooltip. * Components typically would not override this method, * but it can be used to * cause different tooltips to be displayed differently. * * @return the JToolTip used to display this toolTip */ public JToolTip createToolTip() { JToolTip tip = new JToolTip(); tip.setComponent(this); return tip; } /** * Forwards the scrollRectToVisible() message to the * JComponent's parent. Components that can service * the request, such as JViewport, * override this method and perform the scrolling. * * @param aRect the visible Rectangle * @see JViewport */ public void scrollRectToVisible(Rectangle aRect) { Container parent; int dx = getX(), dy = getY(); for (parent = getParent(); !(parent == null) && !(parent instanceof JComponent) && !(parent instanceof CellRendererPane); parent = parent.getParent()) { Rectangle bounds = parent.getBounds(); dx += bounds.x; dy += bounds.y; } if (!(parent == null) && !(parent instanceof CellRendererPane)) { aRect.x += dx; aRect.y += dy; ((JComponent)parent).scrollRectToVisible(aRect); aRect.x -= dx; aRect.y -= dy; } } /** * Sets the autoscrolls property. * If true mouse dragged events will be * synthetically generated when the mouse is dragged * outside of the component's bounds and mouse motion * has paused (while the button continues to be held * down). The synthetic events make it appear that the * drag gesture has resumed in the direction established when * the component's boundary was crossed. Components that * support autoscrolling must handle mouseDragged * events by calling scrollRectToVisible with a * rectangle that contains the mouse event's location. All of * the Swing components that support item selection and are * typically displayed in a JScrollPane * (JTable, JList, JTree, * JTextArea, and JEditorPane) * already handle mouse dragged events in this way. To enable * autoscrolling in any other component, add a mouse motion * listener that calls scrollRectToVisible. * For example, given a JPanel, myPanel: *

     * MouseMotionListener doScrollRectToVisible = new MouseMotionAdapter() {
     *     public void mouseDragged(MouseEvent e) {
     *        Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
     *        ((JPanel)e.getSource()).scrollRectToVisible(r);
     *    }
     * };
     * myPanel.addMouseMotionListener(doScrollRectToVisible);
     * 
* The default value of the autoScrolls * property is false. * * @param autoscrolls if true, synthetic mouse dragged events * are generated when the mouse is dragged outside of a component's * bounds and the mouse button continues to be held down; otherwise * false * @see #getAutoscrolls * @see JViewport * @see JScrollPane * * @beaninfo * expert: true * description: Determines if this component automatically scrolls its contents when dragged. */ public void setAutoscrolls(boolean autoscrolls) { setFlag(AUTOSCROLLS_SET, true); if (this.autoscrolls != autoscrolls) { this.autoscrolls = autoscrolls; if (autoscrolls) { enableEvents(AWTEvent.MOUSE_EVENT_MASK); enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); } else { Autoscroller.stop(this); } } } /** * Gets the autoscrolls property. * * @return the value of the autoscrolls property * @see JViewport * @see #setAutoscrolls */ public boolean getAutoscrolls() { return autoscrolls; } /** * Sets the transferHandler property, * which is null if the component does * not support data transfer operations. *

* If newHandler is not null, * and the system property * suppressSwingDropSupport is not true, this will * install a DropTarget on the JComponent. * The default for the system property is false, so that a * DropTarget will be added. *

* Please see * * How to Use Drag and Drop and Data Transfer, * a section in The Java Tutorial, for more information. * * @param newHandler mechanism for transfer of data to * and from the component * * @see TransferHandler * @see #getTransferHandler * @since 1.4 * @beaninfo * bound: true * hidden: true * description: Mechanism for transfer of data to and from the component */ public void setTransferHandler(TransferHandler newHandler) { TransferHandler oldHandler = (TransferHandler)getClientProperty( TRANSFER_HANDLER_KEY); putClientProperty(TRANSFER_HANDLER_KEY, newHandler); if (! getSuppressDropTarget()) { DropTarget dropHandler = getDropTarget(); if ((dropHandler == null) || (dropHandler instanceof UIResource)) { if (newHandler == null) { setDropTarget(null); } else if (!GraphicsEnvironment.isHeadless()) { setDropTarget(new TransferHandler.SwingDropTarget(this)); } } } firePropertyChange("transferHandler", oldHandler, newHandler); } /** * Gets the transferHandler property. * * @return the value of the transferHandler property * * @see TransferHandler * @see #setTransferHandler * @since 1.4 */ public TransferHandler getTransferHandler() { return (TransferHandler)getClientProperty(TRANSFER_HANDLER_KEY); } /** * Processes mouse events occurring on this component by * dispatching them to any registered * MouseListener objects, refer to * {@link java.awt.Component#processMouseEvent(MouseEvent)} * for a complete description of this method. * * @param e the mouse event * @see java.awt.Component#processMouseEvent * @since 1.5 */ protected void processMouseEvent(MouseEvent e) { if (autoscrolls && e.getID() == MouseEvent.MOUSE_RELEASED) { Autoscroller.stop(this); } super.processMouseEvent(e); } /** * Processes mouse motion events, such as MouseEvent.MOUSE_DRAGGED. * * @param e the MouseEvent * @see MouseEvent */ protected void processMouseMotionEvent(MouseEvent e) { boolean dispatch = true; if (autoscrolls && e.getID() == MouseEvent.MOUSE_DRAGGED) { // We don't want to do the drags when the mouse moves if we're // autoscrolling. It makes it feel spastic. dispatch = !Autoscroller.isRunning(this); Autoscroller.processMouseDragged(e); } if (dispatch) { super.processMouseMotionEvent(e); } } // Inner classes can't get at this method from a super class void superProcessMouseMotionEvent(MouseEvent e) { super.processMouseMotionEvent(e); } /** * This is invoked by the RepaintManager if * createImage is called on the component. * * @param newValue true if the double buffer image was created from this component */ void setCreatedDoubleBuffer(boolean newValue) { setFlag(CREATED_DOUBLE_BUFFER, newValue); } /** * Returns true if the RepaintManager * created the double buffer image from the component. * * @return true if this component had a double buffer image, false otherwise */ boolean getCreatedDoubleBuffer() { return getFlag(CREATED_DOUBLE_BUFFER); } /** * ActionStandin is used as a standin for * ActionListeners that are * added via registerKeyboardAction. */ final class ActionStandin implements Action { private final ActionListener actionListener; private final String command; // This will be non-null if actionListener is an Action. private final Action action; ActionStandin(ActionListener actionListener, String command) { this.actionListener = actionListener; if (actionListener instanceof Action) { this.action = (Action)actionListener; } else { this.action = null; } this.command = command; } public Object getValue(String key) { if (key != null) { if (key.equals(Action.ACTION_COMMAND_KEY)) { return command; } if (action != null) { return action.getValue(key); } if (key.equals(NAME)) { return "ActionStandin"; } } return null; } public boolean isEnabled() { if (actionListener == null) { // This keeps the old semantics where // registerKeyboardAction(null) would essentialy remove // the binding. We don't remove the binding from the // InputMap as that would still allow parent InputMaps // bindings to be accessed. return false; } if (action == null) { return true; } return action.isEnabled(); } public void actionPerformed(ActionEvent ae) { if (actionListener != null) { actionListener.actionPerformed(ae); } } // We don't allow any values to be added. public void putValue(String key, Object value) {} // Does nothing, our enabledness is determiend from our asociated // action. public void setEnabled(boolean b) { } public void addPropertyChangeListener (PropertyChangeListener listener) {} public void removePropertyChangeListener (PropertyChangeListener listener) {} } // This class is used by the KeyboardState class to provide a single // instance that can be stored in the AppContext. static final class IntVector { int array[] = null; int count = 0; int capacity = 0; int size() { return count; } int elementAt(int index) { return array[index]; } void addElement(int value) { if (count == capacity) { capacity = (capacity + 2) * 2; int[] newarray = new int[capacity]; if (count > 0) { System.arraycopy(array, 0, newarray, 0, count); } array = newarray; } array[count++] = value; } void setElementAt(int value, int index) { array[index] = value; } } static class KeyboardState implements Serializable { private static final Object keyCodesKey = JComponent.KeyboardState.class; // Get the array of key codes from the AppContext. static IntVector getKeyCodeArray() { IntVector iv = (IntVector)SwingUtilities.appContextGet(keyCodesKey); if (iv == null) { iv = new IntVector(); SwingUtilities.appContextPut(keyCodesKey, iv); } return iv; } static void registerKeyPressed(int keyCode) { IntVector kca = getKeyCodeArray(); int count = kca.size(); int i; for(i=0;ijava.awt.Component.setEnabled(boolean). */ @Deprecated public void enable() { if (isEnabled() != true) { super.enable(); if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.ENABLED); } } } /** * @deprecated As of JDK version 1.1, * replaced by java.awt.Component.setEnabled(boolean). */ @Deprecated public void disable() { if (isEnabled() != false) { super.disable(); if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.ENABLED, null); } } } /** * The AccessibleContext associated with this * JComponent. */ protected AccessibleContext accessibleContext = null; /** * Returns the AccessibleContext associated with this * JComponent. The method implemented by this base * class returns null. Classes that extend JComponent * should implement this method to return the * AccessibleContext associated with the subclass. * * @return the AccessibleContext of this * JComponent */ public AccessibleContext getAccessibleContext() { return accessibleContext; } /** * Inner class of JComponent used to provide default support for * accessibility. This class is not meant to be used directly by * application developers, but is instead meant only to be * subclassed by component developers. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeansTM * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. */ public abstract class AccessibleJComponent extends AccessibleAWTContainer implements AccessibleExtendedComponent { /** * Though the class is abstract, this should be called by * all sub-classes. */ protected AccessibleJComponent() { super(); } protected ContainerListener accessibleContainerHandler = null; protected FocusListener accessibleFocusHandler = null; /** * Fire PropertyChange listener, if one is registered, * when children added/removed. */ protected class AccessibleContainerHandler implements ContainerListener { public void componentAdded(ContainerEvent e) { Component c = e.getChild(); if (c != null && c instanceof Accessible) { AccessibleJComponent.this.firePropertyChange( AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, null, ((Accessible) c).getAccessibleContext()); } } public void componentRemoved(ContainerEvent e) { Component c = e.getChild(); if (c != null && c instanceof Accessible) { AccessibleJComponent.this.firePropertyChange( AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, ((Accessible) c).getAccessibleContext(), null); } } } /** * Fire PropertyChange listener, if one is registered, * when focus events happen */ protected class AccessibleFocusHandler implements FocusListener { public void focusGained(FocusEvent event) { if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.FOCUSED); } } public void focusLost(FocusEvent event) { if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null); } } } // inner class AccessibleFocusHandler /** * Adds a PropertyChangeListener to the listener list. * * @param listener the PropertyChangeListener to be added */ public void addPropertyChangeListener(PropertyChangeListener listener) { if (accessibleFocusHandler == null) { accessibleFocusHandler = new AccessibleFocusHandler(); JComponent.this.addFocusListener(accessibleFocusHandler); } if (accessibleContainerHandler == null) { accessibleContainerHandler = new AccessibleContainerHandler(); JComponent.this.addContainerListener(accessibleContainerHandler); } super.addPropertyChangeListener(listener); } /** * Removes a PropertyChangeListener from the listener list. * This removes a PropertyChangeListener that was registered * for all properties. * * @param listener the PropertyChangeListener to be removed */ public void removePropertyChangeListener(PropertyChangeListener listener) { if (accessibleFocusHandler != null) { JComponent.this.removeFocusListener(accessibleFocusHandler); accessibleFocusHandler = null; } super.removePropertyChangeListener(listener); } /** * Recursively search through the border hierarchy (if it exists) * for a TitledBorder with a non-null title. This does a depth * first search on first the inside borders then the outside borders. * The assumption is that titles make really pretty inside borders * but not very pretty outside borders in compound border situations. * It's rather arbitrary, but hopefully decent UI programmers will * not create multiple titled borders for the same component. */ protected String getBorderTitle(Border b) { String s; if (b instanceof TitledBorder) { return ((TitledBorder) b).getTitle(); } else if (b instanceof CompoundBorder) { s = getBorderTitle(((CompoundBorder) b).getInsideBorder()); if (s == null) { s = getBorderTitle(((CompoundBorder) b).getOutsideBorder()); } return s; } else { return null; } } // AccessibleContext methods // /** * Gets the accessible name of this object. This should almost never * return java.awt.Component.getName(), as that generally isn't * a localized name, and doesn't have meaning for the user. If the * object is fundamentally a text object (such as a menu item), the * accessible name should be the text of the object (for example, * "save"). * If the object has a tooltip, the tooltip text may also be an * appropriate String to return. * * @return the localized name of the object -- can be null if this * object does not have a name * @see AccessibleContext#setAccessibleName */ public String getAccessibleName() { String name = accessibleName; // fallback to the titled border if it exists // if (name == null) { name = getBorderTitle(getBorder()); } // fallback to the label labeling us if it exists // if (name == null) { Object o = getClientProperty(JLabel.LABELED_BY_PROPERTY); if (o instanceof Accessible) { AccessibleContext ac = ((Accessible) o).getAccessibleContext(); if (ac != null) { name = ac.getAccessibleName(); } } } return name; } /** * Gets the accessible description of this object. This should be * a concise, localized description of what this object is - what * is its meaning to the user. If the object has a tooltip, the * tooltip text may be an appropriate string to return, assuming * it contains a concise description of the object (instead of just * the name of the object - for example a "Save" icon on a toolbar that * had "save" as the tooltip text shouldn't return the tooltip * text as the description, but something like "Saves the current * text document" instead). * * @return the localized description of the object -- can be null if * this object does not have a description * @see AccessibleContext#setAccessibleDescription */ public String getAccessibleDescription() { String description = accessibleDescription; // fallback to the tool tip text if it exists // if (description == null) { try { description = getToolTipText(); } catch (Exception e) { // Just in case the subclass overrode the // getToolTipText method and actually // requires a MouseEvent. // [[[FIXME: WDW - we probably should require this // method to take a MouseEvent and just pass it on // to getToolTipText. The swing-feedback traffic // leads me to believe getToolTipText might change, // though, so I was hesitant to make this change at // this time.]]] } } // fallback to the label labeling us if it exists // if (description == null) { Object o = getClientProperty(JLabel.LABELED_BY_PROPERTY); if (o instanceof Accessible) { AccessibleContext ac = ((Accessible) o).getAccessibleContext(); if (ac != null) { description = ac.getAccessibleDescription(); } } } return description; } /** * Gets the role of this object. * * @return an instance of AccessibleRole describing the role of the * object * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { return AccessibleRole.SWING_COMPONENT; } /** * Gets the state of this object. * * @return an instance of AccessibleStateSet containing the current * state set of the object * @see AccessibleState */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = super.getAccessibleStateSet(); if (JComponent.this.isOpaque()) { states.add(AccessibleState.OPAQUE); } return states; } /** * Returns the number of accessible children in the object. If all * of the children of this object implement Accessible, than this * method should return the number of children of this object. * * @return the number of accessible children in the object. */ public int getAccessibleChildrenCount() { return super.getAccessibleChildrenCount(); } /** * Returns the nth Accessible child of the object. * * @param i zero-based index of child * @return the nth Accessible child of the object */ public Accessible getAccessibleChild(int i) { return super.getAccessibleChild(i); } // ----- AccessibleExtendedComponent /** * Returns the AccessibleExtendedComponent * * @return the AccessibleExtendedComponent */ AccessibleExtendedComponent getAccessibleExtendedComponent() { return this; } /** * Returns the tool tip text * * @return the tool tip text, if supported, of the object; * otherwise, null */ public String getToolTipText() { return null; } /** * Returns the titled border text * * @return the titled border text, if supported, of the object; * otherwise, null */ public String getTitledBorderText() { Border border = JComponent.this.getBorder(); if (border instanceof TitledBorder) { return ((TitledBorder)border).getTitle(); } else { return null; } } /** * Returns key bindings associated with this object * * @return the key bindings, if supported, of the object; * otherwise, null * @see AccessibleKeyBinding */ public AccessibleKeyBinding getAccessibleKeyBinding() { return null; } } // inner class AccessibleJComponent /** * Returns an ArrayTable used for * key/value "client properties" for this component. If the * clientProperties table doesn't exist, an empty one * will be created. * * @return an ArrayTable * @see #putClientProperty * @see #getClientProperty */ private ArrayTable getClientProperties() { if (clientProperties == null) { clientProperties = new ArrayTable(); } return clientProperties; } /** * Returns the value of the property with the specified key. Only * properties added with putClientProperty will return * a non-null value. * * @param key the being queried * @return the value of this property or null * @see #putClientProperty */ public final Object getClientProperty(Object key) { if (key == SwingUtilities2.AA_TEXT_PROPERTY_KEY) { return Boolean.valueOf(aaText); } if(clientProperties == null) { return null; } else { synchronized(clientProperties) { return clientProperties.get(key); } } } /** * Adds an arbitrary key/value "client property" to this component. *

* The get/putClientProperty methods provide access to * a small per-instance hashtable. Callers can use get/putClientProperty * to annotate components that were created by another module. * For example, a * layout manager might store per child constraints this way. For example: *

     * componentA.putClientProperty("to the left of", componentB);
     * 
* If value is null this method will remove the property. * Changes to client properties are reported with * PropertyChange events. * The name of the property (for the sake of PropertyChange * events) is key.toString(). *

* The clientProperty dictionary is not intended to * support large * scale extensions to JComponent nor should be it considered an * alternative to subclassing when designing a new component. * * @param key the new client property key * @param value the new client property value; if null * this method will remove the property * @see #getClientProperty * @see #addPropertyChangeListener */ public final void putClientProperty(Object key, Object value) { if (value == null && clientProperties == null) { // Both the value and ArrayTable are null, implying we don't // have to do anything. return; } if (key == SwingUtilities2.AA_TEXT_PROPERTY_KEY) { if (value instanceof Boolean) { aaText = ((Boolean)value).booleanValue(); } return; } ArrayTable clientProperties = getClientProperties(); Object oldValue; synchronized(clientProperties) { oldValue = clientProperties.get(key); if (value != null) { clientProperties.put(key, value); } else if (oldValue != null) { clientProperties.remove(key); } else { // old == new == null return; } } firePropertyChange(key.toString(), oldValue, value); } /* * Sets the property with the specified name to the specified value if * the property has not already been set by the client program. * This method is used primarily to set UI defaults for properties * with primitive types, where the values cannot be marked with * UIResource. * @see LookAndFeel#installProperty * @param propertyName String containing the name of the property * @param value Object containing the property value */ void setUIProperty(String propertyName, Object value) { if (propertyName == "opaque") { if (!getFlag(OPAQUE_SET)) { setOpaque(((Boolean)value).booleanValue()); setFlag(OPAQUE_SET, false); } } else if (propertyName == "autoscrolls") { if (!getFlag(AUTOSCROLLS_SET)) { setAutoscrolls(((Boolean)value).booleanValue()); setFlag(AUTOSCROLLS_SET, false); } } else if (propertyName == "focusTraversalKeysForward") { if (!getFlag(FOCUS_TRAVERSAL_KEYS_FORWARD_SET)) { super.setFocusTraversalKeys(KeyboardFocusManager. FORWARD_TRAVERSAL_KEYS, (Set)value); } } else if (propertyName == "focusTraversalKeysBackward") { if (!getFlag(FOCUS_TRAVERSAL_KEYS_BACKWARD_SET)) { super.setFocusTraversalKeys(KeyboardFocusManager. BACKWARD_TRAVERSAL_KEYS, (Set)value); } } else { throw new IllegalArgumentException("property \""+ propertyName+ "\" cannot be set using this method"); } } /** * Sets the focus traversal keys for a given traversal operation for this * Component. * Refer to * {@link java.awt.Component#setFocusTraversalKeys} * for a complete description of this method. * * @param id one of KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, or * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS * @param keystrokes the Set of AWTKeyStroke for the specified operation * @see java.awt.KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS * @see java.awt.KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS * @see java.awt.KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS * @throws IllegalArgumentException if id is not one of * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, or * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, or if keystrokes * contains null, or if any Object in keystrokes is not an * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, * or if any keystroke already maps to another focus traversal * operation for this Component * @since 1.5 * @beaninfo * bound: true */ public void setFocusTraversalKeys(int id, Set keystrokes) { if (id == KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS) { setFlag(FOCUS_TRAVERSAL_KEYS_FORWARD_SET,true); } else if (id == KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS) { setFlag(FOCUS_TRAVERSAL_KEYS_BACKWARD_SET,true); } super.setFocusTraversalKeys(id,keystrokes); } /* --- Transitional java.awt.Component Support --- * The methods and fields in this section will migrate to * java.awt.Component in the next JDK release. */ /** * Returns true if this component is lightweight, that is, if it doesn't * have a native window system peer. * * @return true if this component is lightweight */ public static boolean isLightweightComponent(Component c) { return c.getPeer() instanceof LightweightPeer; } /** * @deprecated As of JDK 5, * replaced by Component.setBounds(int, int, int, int). *

* Moves and resizes this component. * * @param x the new horizontal location * @param y the new vertical location * @param w the new width * @param h the new height * @see java.awt.Component#setBounds */ @Deprecated public void reshape(int x, int y, int w, int h) { super.reshape(x, y, w, h); } /** * Stores the bounds of this component into "return value" * rv and returns rv. * If rv is null a new Rectangle * is allocated. This version of getBounds is useful * if the caller wants to avoid allocating a new Rectangle * object on the heap. * * @param rv the return value, modified to the component's bounds * @return rv; if rv is null * return a newly created Rectangle with this * component's bounds */ public Rectangle getBounds(Rectangle rv) { if (rv == null) { return new Rectangle(getX(), getY(), getWidth(), getHeight()); } else { rv.setBounds(getX(), getY(), getWidth(), getHeight()); return rv; } } /** * Stores the width/height of this component into "return value" * rv and returns rv. * If rv is null a new Dimension * object is allocated. This version of getSize * is useful if the caller wants to avoid allocating a new * Dimension object on the heap. * * @param rv the return value, modified to the component's size * @return rv */ public Dimension getSize(Dimension rv) { if (rv == null) { return new Dimension(getWidth(), getHeight()); } else { rv.setSize(getWidth(), getHeight()); return rv; } } /** * Stores the x,y origin of this component into "return value" * rv and returns rv. * If rv is null a new Point * is allocated. This version of getLocation is useful * if the caller wants to avoid allocating a new Point * object on the heap. * * @param rv the return value, modified to the component's location * @return rv */ public Point getLocation(Point rv) { if (rv == null) { return new Point(getX(), getY()); } else { rv.setLocation(getX(), getY()); return rv; } } /** * Returns the current x coordinate of the component's origin. * This method is preferable to writing * component.getBounds().x, or * component.getLocation().x because it doesn't cause any * heap allocations. * * @return the current x coordinate of the component's origin */ public int getX() { return super.getX(); } /** * Returns the current y coordinate of the component's origin. * This method is preferable to writing * component.getBounds().y, or * component.getLocation().y because it doesn't cause any * heap allocations. * * @return the current y coordinate of the component's origin */ public int getY() { return super.getY(); } /** * Returns the current width of this component. * This method is preferable to writing * component.getBounds().width, or * component.getSize().width because it doesn't cause any * heap allocations. * * @return the current width of this component */ public int getWidth() { return super.getWidth(); } /** * Returns the current height of this component. * This method is preferable to writing * component.getBounds().height, or * component.getSize().height because it doesn't cause any * heap allocations. * * @return the current height of this component */ public int getHeight() { return super.getHeight(); } /** * Returns true if this component is completely opaque. *

* An opaque component paints every pixel within its * rectangular bounds. A non-opaque component paints only a subset of * its pixels or none at all, allowing the pixels underneath it to * "show through". Therefore, a component that does not fully paint * its pixels provides a degree of transparency. *

* Subclasses that guarantee to always completely paint their contents * should override this method and return true. * * @return true if this component is completely opaque * @see #setOpaque */ public boolean isOpaque() { return getFlag(IS_OPAQUE); } /** * If true the component paints every pixel within its bounds. * Otherwise, the component may not paint some or all of its * pixels, allowing the underlying pixels to show through. *

* The default value of this property is false for JComponent. * However, the default value for this property on most standard * JComponent subclasses (such as JButton and * JTree) is look-and-feel dependent. * * @param isOpaque true if this component should be opaque * @see #isOpaque * @beaninfo * bound: true * expert: true * description: The component's opacity */ public void setOpaque(boolean isOpaque) { boolean oldValue = getFlag(IS_OPAQUE); setFlag(IS_OPAQUE, isOpaque); setFlag(OPAQUE_SET, true); firePropertyChange("opaque", oldValue, isOpaque); } /** * If the specified rectangle is completely obscured by any of this * component's opaque children then returns true. Only direct children * are considered, more distant descendants are ignored. A * JComponent is opaque if * JComponent.isOpaque() returns true, other lightweight * components are always considered transparent, and heavyweight components * are always considered opaque. * * @param x x value of specified rectangle * @param y y value of specified rectangle * @param width width of specified rectangle * @param height height of specified rectangle * @return true if the specified rectangle is obscured by an opaque child */ boolean rectangleIsObscured(int x,int y,int width,int height) { int numChildren = getComponentCount(); for(int i = 0; i < numChildren; i++) { Component child = getComponent(i); int cx, cy, cw, ch; cx = child.getX(); cy = child.getY(); cw = child.getWidth(); ch = child.getHeight(); if (x >= cx && (x + width) <= (cx + cw) && y >= cy && (y + height) <= (cy + ch) && child.isVisible()) { if(child instanceof JComponent) { // System.out.println("A) checking opaque: " + ((JComponent)child).isOpaque() + " " + child); // System.out.print("B) "); // Thread.dumpStack(); return ((JComponent)child).isOpaque(); } else { /** Sometimes a heavy weight can have a bound larger than its peer size * so we should always draw under heavy weights */ return false; } } } return false; } /** * Returns the Component's "visible rect rectangle" - the * intersection of the visible rectangles for the component c * and all of its ancestors. The return value is stored in * visibleRect. * * @param c the component * @param visibleRect a Rectangle computed as the * intersection of all visible rectangles for the component * c and all of its ancestors -- this is the * return value for this method * @see #getVisibleRect */ static final void computeVisibleRect(Component c, Rectangle visibleRect) { Container p = c.getParent(); Rectangle bounds = c.getBounds(); if (p == null || p instanceof Window || p instanceof Applet) { visibleRect.setBounds(0, 0, bounds.width, bounds.height); } else { computeVisibleRect(p, visibleRect); visibleRect.x -= bounds.x; visibleRect.y -= bounds.y; SwingUtilities.computeIntersection(0,0,bounds.width,bounds.height,visibleRect); } } /** * Returns the Component's "visible rect rectangle" - the * intersection of the visible rectangles for this component * and all of its ancestors. The return value is stored in * visibleRect. * * @param visibleRect a Rectangle computed as the * intersection of all visible rectangles for this * component and all of its ancestors -- this is the return * value for this method * @see #getVisibleRect */ public void computeVisibleRect(Rectangle visibleRect) { computeVisibleRect(this, visibleRect); } /** * Returns the Component's "visible rectangle" - the * intersection of this component's visible rectangle, * new Rectangle(0, 0, getWidth(), getHeight()), * and all of its ancestors' visible rectangles. * * @return the visible rectangle */ public Rectangle getVisibleRect() { Rectangle visibleRect = new Rectangle(); computeVisibleRect(visibleRect); return visibleRect; } /** * Support for reporting bound property changes for boolean properties. * This method can be called when a bound property has changed and it will * send the appropriate PropertyChangeEvent to any registered * PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { super.firePropertyChange(propertyName, oldValue, newValue); } /** * Support for reporting bound property changes for integer properties. * This method can be called when a bound property has changed and it will * send the appropriate PropertyChangeEvent to any registered * PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { super.firePropertyChange(propertyName, oldValue, newValue); } // XXX This method is implemented as a workaround to a JLS issue with ambiguous // methods. This should be removed once 4758654 is resolved. public void firePropertyChange(String propertyName, char oldValue, char newValue) { super.firePropertyChange(propertyName, oldValue, newValue); } /** * Supports reporting constrained property changes. * This method can be called when a constrained property has changed * and it will send the appropriate PropertyChangeEvent * to any registered VetoableChangeListeners. * * @param propertyName the name of the property that was listened on * @param oldValue the old value of the property * @param newValue the new value of the property * @exception PropertyVetoException when the attempt to set the * property is vetoed by the component */ protected void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws java.beans.PropertyVetoException { if (vetoableChangeSupport == null) { return; } vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue); } /** * Adds a VetoableChangeListener to the listener list. * The listener is registered for all properties. * * @param listener the VetoableChangeListener to be added */ public synchronized void addVetoableChangeListener(VetoableChangeListener listener) { if (vetoableChangeSupport == null) { vetoableChangeSupport = new java.beans.VetoableChangeSupport(this); } vetoableChangeSupport.addVetoableChangeListener(listener); } /** * Removes a VetoableChangeListener from the listener list. * This removes a VetoableChangeListener that was registered * for all properties. * * @param listener the VetoableChangeListener to be removed */ public synchronized void removeVetoableChangeListener(VetoableChangeListener listener) { if (vetoableChangeSupport == null) { return; } vetoableChangeSupport.removeVetoableChangeListener(listener); } /** * Returns an array of all the vetoable change listeners * registered on this component. * * @return all of the component's VetoableChangeListeners * or an empty * array if no vetoable change listeners are currently registered * * @see #addVetoableChangeListener * @see #removeVetoableChangeListener * * @since 1.4 */ public synchronized VetoableChangeListener[] getVetoableChangeListeners() { if (vetoableChangeSupport == null) { return new VetoableChangeListener[0]; } return vetoableChangeSupport.getVetoableChangeListeners(); } /** * Returns the top-level ancestor of this component (either the * containing Window or Applet), * or null if this component has not * been added to any container. * * @return the top-level Container that this component is in, * or null if not in any container */ public Container getTopLevelAncestor() { for(Container p = this; p != null; p = p.getParent()) { if(p instanceof Window || p instanceof Applet) { return p; } } return null; } private AncestorNotifier getAncestorNotifier() { return (AncestorNotifier)getClientProperty(ANCESTOR_NOTIFIER_KEY); } /** * Registers listener so that it will receive * AncestorEvents when it or any of its ancestors * move or are made visible or invisible. * Events are also sent when the component or its ancestors are added * or removed from the containment hierarchy. * * @param listener the AncestorListener to register * @see AncestorEvent */ public void addAncestorListener(AncestorListener listener) { AncestorNotifier ancestorNotifier = getAncestorNotifier(); if (ancestorNotifier == null) { ancestorNotifier = new AncestorNotifier(this); putClientProperty(ANCESTOR_NOTIFIER_KEY, ancestorNotifier); } ancestorNotifier.addAncestorListener(listener); } /** * Unregisters listener so that it will no longer receive * AncestorEvents. * * @param listener the AncestorListener to be removed * @see #addAncestorListener */ public void removeAncestorListener(AncestorListener listener) { AncestorNotifier ancestorNotifier = getAncestorNotifier(); if (ancestorNotifier == null) { return; } ancestorNotifier.removeAncestorListener(listener); if (ancestorNotifier.listenerList.getListenerList().length == 0) { ancestorNotifier.removeAllListeners(); putClientProperty(ANCESTOR_NOTIFIER_KEY, null); } } /** * Returns an array of all the ancestor listeners * registered on this component. * * @return all of the component's AncestorListeners * or an empty * array if no ancestor listeners are currently registered * * @see #addAncestorListener * @see #removeAncestorListener * * @since 1.4 */ public AncestorListener[] getAncestorListeners() { AncestorNotifier ancestorNotifier = getAncestorNotifier(); if (ancestorNotifier == null) { return new AncestorListener[0]; } return ancestorNotifier.getAncestorListeners(); } /** * Returns an array of all the objects currently registered * as FooListeners * upon this JComponent. * FooListeners are registered using the * addFooListener method. * *

* * You can specify the listenerType argument * with a class literal, * such as * FooListener.class. * For example, you can query a * JComponent c * for its mouse listeners with the following code: *

MouseListener[] mls = (MouseListener[])(c.getListeners(MouseListener.class));
* If no such listeners exist, this method returns an empty array. * * @param listenerType the type of listeners requested; this parameter * should specify an interface that descends from * java.util.EventListener * @return an array of all objects registered as * FooListeners on this component, * or an empty array if no such * listeners have been added * @exception ClassCastException if listenerType * doesn't specify a class or interface that implements * java.util.EventListener * * @since 1.3 * * @see #getVetoableChangeListeners * @see #getAncestorListeners */ public T[] getListeners(Class listenerType) { T[] result; if (listenerType == AncestorListener.class) { // AncestorListeners are handled by the AncestorNotifier result = (T[])getAncestorListeners(); } else if (listenerType == VetoableChangeListener.class) { // VetoableChangeListeners are handled by VetoableChangeSupport result = (T[])getVetoableChangeListeners(); } else if (listenerType == PropertyChangeListener.class) { // PropertyChangeListeners are handled by PropertyChangeSupport result = (T[])getPropertyChangeListeners(); } else { result = (T[])listenerList.getListeners(listenerType); } if (result.length == 0) { return super.getListeners(listenerType); } return result; } /** * Notifies this component that it now has a parent component. * When this method is invoked, the chain of parent components is * set up with KeyboardAction event listeners. * * @see #registerKeyboardAction */ public void addNotify() { super.addNotify(); firePropertyChange("ancestor", null, getParent()); registerWithKeyboardManager(false); registerNextFocusableComponent(); } /** * Notifies this component that it no longer has a parent component. * When this method is invoked, any KeyboardActions * set up in the the chain of parent components are removed. * * @see #registerKeyboardAction */ public void removeNotify() { super.removeNotify(); // This isn't strictly correct. The event shouldn't be // fired until *after* the parent is set to null. But // we only get notified before that happens firePropertyChange("ancestor", getParent(), null); unregisterWithKeyboardManager(); deregisterNextFocusableComponent(); if (getCreatedDoubleBuffer()) { RepaintManager.currentManager(this).resetDoubleBuffer(); setCreatedDoubleBuffer(false); } if (autoscrolls) { Autoscroller.stop(this); } } /** * Adds the specified region to the dirty region list if the component * is showing. The component will be repainted after all of the * currently pending events have been dispatched. * * @param tm this parameter is not used * @param x the x value of the dirty region * @param y the y value of the dirty region * @param width the width of the dirty region * @param height the height of the dirty region * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); } /** * Adds the specified region to the dirty region list if the component * is showing. The component will be repainted after all of the * currently pending events have been dispatched. * * @param r a Rectangle containing the dirty region * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ public void repaint(Rectangle r) { repaint(0,r.x,r.y,r.width,r.height); } /** * Supports deferred automatic layout. *

* Calls invalidate and then adds this component's * validateRoot to a list of components that need to be * validated. Validation will occur after all currently pending * events have been dispatched. In other words after this method * is called, the first validateRoot (if any) found when walking * up the containment hierarchy of this component will be validated. * By default, JRootPane, JScrollPane, * and JTextField return true * from isValidateRoot. *

* This method will automatically be called on this component * when a property value changes such that size, location, or * internal layout of this component has been affected. This automatic * updating differs from the AWT because programs generally no * longer need to invoke validate to get the contents of the * GUI to update. *

* * @see java.awt.Component#invalidate * @see java.awt.Container#validate * @see #isValidateRoot * @see RepaintManager#addInvalidComponent */ public void revalidate() { if (getParent() == null) { // Note: We don't bother invalidating here as once added // to a valid parent invalidate will be invoked (addImpl // invokes addNotify which will invoke invalidate on the // new Component). Also, if we do add a check to isValid // here it can potentially be called before the constructor // which was causing some people grief. return; } if (SwingUtilities.isEventDispatchThread()) { invalidate(); RepaintManager.currentManager(this).addInvalidComponent(this); } else { Runnable callRevalidate = new Runnable() { public void run() { revalidate(); } }; SwingUtilities.invokeLater(callRevalidate); } } /** * If this method returns true, revalidate calls by * descendants of this component will cause the entire tree * beginning with this root to be validated. * Returns false by default. JScrollPane overrides * this method and returns true. * * @return always returns false * @see #revalidate * @see java.awt.Component#invalidate * @see java.awt.Container#validate */ public boolean isValidateRoot() { return false; } /** * Returns true if this component tiles its children -- that is, if * it can guarantee that the children will not overlap. The * repainting system is substantially more efficient in this * common case. JComponent subclasses that can't make this * guarantee, such as JLayeredPane, * should override this method to return false. * * @return always returns true */ public boolean isOptimizedDrawingEnabled() { return true; } /** * Returns true if a paint triggered on a child component should cause * painting to originate from this Component, or one of its ancestors. * * @return true if painting should originate from this Component or * one of its ancestors. */ boolean isPaintingOrigin() { return false; } /** * Paints the specified region in this component and all of its * descendants that overlap the region, immediately. *

* It's rarely necessary to call this method. In most cases it's * more efficient to call repaint, which defers the actual painting * and can collapse redundant requests into a single paint call. * This method is useful if one needs to update the display while * the current event is being dispatched. * * @param x the x value of the region to be painted * @param y the y value of the region to be painted * @param w the width of the region to be painted * @param h the height of the region to be painted * @see #repaint */ public void paintImmediately(int x,int y,int w, int h) { Component c = this; Component parent; if(!isShowing()) { return; } while(!((JComponent)c).isOpaque()) { parent = c.getParent(); if(parent != null) { x += c.getX(); y += c.getY(); c = parent; } else { break; } if(!(c instanceof JComponent)) { break; } } if(c instanceof JComponent) { ((JComponent)c)._paintImmediately(x,y,w,h); } else { c.repaint(x,y,w,h); } } /** * Paints the specified region now. * * @param r a Rectangle containing the region to be painted */ public void paintImmediately(Rectangle r) { paintImmediately(r.x,r.y,r.width,r.height); } /** * Returns whether this component should be guaranteed to be on top. * For example, it would make no sense for Menus to pop up * under another component, so they would always return true. * Most components will want to return false, hence that is the default. * * @return always returns false */ // package private boolean alwaysOnTop() { return false; } void setPaintingChild(Component paintingChild) { this.paintingChild = paintingChild; } void _paintImmediately(int x, int y, int w, int h) { Graphics g; Container c; Rectangle b; int tmpX, tmpY, tmpWidth, tmpHeight; int offsetX=0,offsetY=0; boolean hasBuffer = false; JComponent bufferedComponent = null; JComponent paintingComponent = this; RepaintManager repaintManager = RepaintManager.currentManager(this); // parent Container's up to Window or Applet. First container is // the direct parent. Note that in testing it was faster to // alloc a new Vector vs keeping a stack of them around, and gc // seemed to have a minimal effect on this. Vector path = new Vector(7); int pIndex = -1; int pCount = 0; tmpX = tmpY = tmpWidth = tmpHeight = 0; Rectangle paintImmediatelyClip = fetchRectangle(); paintImmediatelyClip.x = x; paintImmediatelyClip.y = y; paintImmediatelyClip.width = w; paintImmediatelyClip.height = h; // System.out.println("1) ************* in _paintImmediately for " + this); boolean ontop = alwaysOnTop() && isOpaque(); Component child; for (c = this, child = null; c != null && !(c instanceof Window) && !(c instanceof Applet); child = c, c = c.getParent()) { JComponent jc = (c instanceof JComponent) ? (JComponent)c : null; path.addElement(c); if(!ontop && jc != null && !jc.isOptimizedDrawingEnabled()) { boolean resetPC; // Children of c may overlap, three possible cases for the // painting region: // . Completely obscured by an opaque sibling, in which // case there is no need to paint. // . Partially obscured by a sibling: need to start // painting from c. // . Otherwise we aren't obscured and thus don't need to // start painting from parent. if (c != this) { if (jc.isPaintingOrigin()) { resetPC = true; } else { Component[] children = c.getComponents(); int i = 0; for (; i 0 ; i--) { comp = (Component) path.elementAt(i); if(comp instanceof JComponent) { ((JComponent)comp).setPaintingChild ((Component)path.elementAt(i-1)); } } } try { try { Graphics pcg = paintingComponent.getGraphics(); g = (pcg == null) ? null : pcg.create(); pcg.dispose(); } catch(NullPointerException e) { g = null; e.printStackTrace(); } if (g == null) { System.err.println("In paintImmediately null graphics"); return; } try { boolean paintCompleted = false; if (hasBuffer) { paintCompleted = paintDoubleBuffered(paintingComponent, bufferedComponent, g, paintImmediatelyClip.x, paintImmediatelyClip.y, paintImmediatelyClip.width, paintImmediatelyClip.height); } if (!paintCompleted) { //System.out.println("has no buffer"); g.setClip(paintImmediatelyClip.x,paintImmediatelyClip.y, paintImmediatelyClip.width,paintImmediatelyClip.height); paintingComponent.paint(g); } } finally { g.dispose(); } } finally { // Reset the painting child for the parent components. if(paintingComponent != this) { Component comp; int i = pIndex; for(; i > 0 ; i--) { comp = (Component) path.elementAt(i); if(comp instanceof JComponent) { ((JComponent)comp).setPaintingChild(null); } } } path.removeAllElements(); paintingComponent.setFlag(IS_REPAINTING, false); } recycleRectangle(paintImmediatelyClip); } private boolean paintDoubleBuffered(JComponent paintingComponent, Component bufferComponent, Graphics g, int clipX, int clipY, int clipW, int clipH) { RepaintManager repaintManager = RepaintManager.currentManager(paintingComponent); boolean paintCompleted = false; Image offscreen = null; // First attempt to use VolatileImage buffer for performance. If this fails // (which should rarely occur), fallback to a standard Image buffer. // if (repaintManager.useVolatileDoubleBuffer() && (offscreen = repaintManager.getVolatileOffscreenBuffer( bufferComponent,clipW,clipH)) != null && (offscreen.getWidth(null) > 0 && offscreen.getHeight(null) > 0)) { VolatileImage vImage = (java.awt.image.VolatileImage)offscreen; GraphicsConfiguration gc = bufferComponent. getGraphicsConfiguration(); for (int i = 0; !paintCompleted && i < RepaintManager.VOLATILE_LOOP_MAX; i++) { if (vImage.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) { repaintManager.resetVolatileDoubleBuffer(gc); offscreen = repaintManager.getVolatileOffscreenBuffer(bufferComponent,clipW, clipH); vImage = (java.awt.image.VolatileImage)offscreen; } paintWithOffscreenBuffer(paintingComponent, g, clipX, clipY, clipW, clipH, offscreen); paintCompleted = !vImage.contentsLost(); } } if (!paintCompleted) { // VolatileImage painting loop failed, fallback to regular offscreen buffer if ((offscreen = repaintManager.getOffscreenBuffer(bufferComponent, clipW, clipH)) != null && (offscreen.getWidth(null) > 0 && offscreen.getHeight(null) > 0)) { paintWithOffscreenBuffer(paintingComponent, g, clipX, clipY, clipW, clipH, offscreen); paintCompleted = true; } } return paintCompleted; } private void paintWithOffscreenBuffer(JComponent paintingComponent, Graphics g, int clipX, int clipY, int clipW, int clipH, Image offscreen) { Graphics og = offscreen.getGraphics(); Graphics osg = (og == null) ? null : og.create(); og.dispose(); int bw = offscreen.getWidth(null); int bh = offscreen.getHeight(null); int x,y,maxx,maxy; if (bw > clipW) { bw = clipW; } if (bh > clipH) { bh = clipH; } try { paintingComponent.setFlag(ANCESTOR_USING_BUFFER,true); paintingComponent.setFlag(IS_PAINTING_TILE,true); for(x = clipX, maxx = clipX+clipW; x < maxx ; x += bw ) { for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) { if ((y+bh) >= maxy && (x+bw) >= maxx) { paintingComponent.setFlag(IS_PAINTING_TILE,false); } osg.translate(-x,-y); osg.setClip(x,y,bw,bh); if (paintingComponent.getFlag(IS_REPAINTING)) { // Called from paintImmediately (RepaintManager) to fill // repaint request paintingComponent.paint(osg); } else { // Called from paint() (AWT) to repair damage if(!paintingComponent.rectangleIsObscured(clipX,clipY,bw,bh)) { paintingComponent.paintComponent(osg); paintingComponent.paintBorder(osg); } paintingComponent.paintChildren(osg); } g.setClip(x,y,bw,bh); g.drawImage(offscreen,x,y,paintingComponent); osg.translate(x,y); } } } finally { paintingComponent.setFlag(ANCESTOR_USING_BUFFER,false); paintingComponent.setFlag(IS_PAINTING_TILE,false); osg.dispose(); } } /** * Returns whether or not the region of the specified component is * obscured by a sibling. * * @return NOT_OBSCURED if non of the siblings above the Component obscure * it, COMPLETELY_OBSCURED if one of the siblings completely * obscures the Component or PARTIALLY_OBSCURED if the Comonent is * only partially obscured. */ private int getObscuredState(int compIndex, int x, int y, int width, int height) { int retValue = NOT_OBSCURED; Rectangle tmpRect = fetchRectangle(); for (int i = compIndex - 1 ; i >= 0 ; i--) { Component sibling = getComponent(i); if (!sibling.isVisible()) { continue; } Rectangle siblingRect; boolean opaque; if (sibling instanceof JComponent) { opaque = ((JComponent)sibling).isOpaque(); if (!opaque) { if (retValue == PARTIALLY_OBSCURED) { continue; } } } else { opaque = true; } siblingRect = sibling.getBounds(tmpRect); if (opaque && x >= siblingRect.x && (x + width) <= (siblingRect.x + siblingRect.width) && y >= siblingRect.y && (y + height) <= (siblingRect.y + siblingRect.height)) { recycleRectangle(tmpRect); return COMPLETELY_OBSCURED; } else if (retValue == NOT_OBSCURED && !((x + width <= siblingRect.x) || (y + height <= siblingRect.y) || (x >= siblingRect.x + siblingRect.width) || (y >= siblingRect.y + siblingRect.height))) { retValue = PARTIALLY_OBSCURED; } } recycleRectangle(tmpRect); return retValue; } /** * Returns true, which implies that before checking if a child should * be painted it is first check that the child is not obscured by another * sibling. This is only checked if isOptimizedDrawingEnabled * returns false. * * @return always returns true */ boolean checkIfChildObscuredBySibling() { return true; } private void setFlag(int aFlag, boolean aValue) { if(aValue) { flags |= (1 << aFlag); } else { flags &= ~(1 << aFlag); } } private boolean getFlag(int aFlag) { int mask = (1 << aFlag); return ((flags & mask) == mask); } // These functions must be static so that they can be called from // subclasses inside the package, but whose inheritance hierarhcy includes // classes outside of the package below JComponent (e.g., JTextArea). static void setWriteObjCounter(JComponent comp, byte count) { comp.flags = (comp.flags & ~(0xFF << WRITE_OBJ_COUNTER_FIRST)) | (count << WRITE_OBJ_COUNTER_FIRST); } static byte getWriteObjCounter(JComponent comp) { return (byte)((comp.flags >> WRITE_OBJ_COUNTER_FIRST) & 0xFF); } /** Buffering **/ /** * Sets whether the this component should use a buffer to paint. * If set to true, all the drawing from this component will be done * in an offscreen painting buffer. The offscreen painting buffer will * the be copied onto the screen. * Swings painting system always uses a maximum of one double buffer. * If a Component is buffered and one of its ancestor * is also buffered, the ancestor buffer will be used. * * @param aFlag if true, set this component to be double buffered */ public void setDoubleBuffered(boolean aFlag) { setFlag(IS_DOUBLE_BUFFERED,aFlag); } /** * Returns whether this component should use a buffer to paint. * * @return true if this component is double buffered, otherwise false */ public boolean isDoubleBuffered() { return getFlag(IS_DOUBLE_BUFFERED); } /** * Returns the JRootPane ancestor for this component. * * @return the JRootPane that contains this component, * or null if no JRootPane is found */ public JRootPane getRootPane() { return SwingUtilities.getRootPane(this); } /** Serialization **/ /** * This is called from Component by way of reflection. Do NOT change * the name unless you change the code in Component as well. */ void compWriteObjectNotify() { byte count = JComponent.getWriteObjCounter(this); JComponent.setWriteObjCounter(this, (byte)(count + 1)); if (count != 0) { return; } if (ui != null) { ui.uninstallUI(this); } /* JTableHeader is in a separate package, which prevents it from * being able to override this package-private method the way the * other components can. We don't want to make this method protected * because it would introduce public-api for a less-than-desirable * serialization scheme, so we compromise with this 'instanceof' hack * for now. */ if (getToolTipText() != null || this instanceof javax.swing.table.JTableHeader) { ToolTipManager.sharedInstance().unregisterComponent(JComponent.this); } } /** * This object is the ObjectInputStream callback * that's called after a complete graph of objects (including at least * one JComponent) has been read. * It sets the UI property of each Swing component * that was read to the current default with updateUI. *

* As each component is read in we keep track of the current set of * root components here, in the roots vector. Note that there's only one * ReadObjectCallback per ObjectInputStream, * they're stored in the static readObjectCallbacks * hashtable. * * @see java.io.ObjectInputStream#registerValidation * @see SwingUtilities#updateComponentTreeUI */ private class ReadObjectCallback implements ObjectInputValidation { private final Vector roots = new Vector(1); private final ObjectInputStream inputStream; ReadObjectCallback(ObjectInputStream s) throws Exception { inputStream = s; s.registerValidation(this, 0); } /** * This is the method that's called after the entire graph * of objects has been read in. It initializes * the UI property of all of the copmonents with * SwingUtilities.updateComponentTreeUI. */ public void validateObject() throws InvalidObjectException { try { for(int i = 0; i < roots.size(); i++) { JComponent root = (JComponent)(roots.elementAt(i)); SwingUtilities.updateComponentTreeUI(root); } } finally { readObjectCallbacks.remove(inputStream); } } /** * If c isn't a descendant of a component we've already * seen, then add it to the roots Vector. * * @param c the JComponent to add */ private void registerComponent(JComponent c) { /* If the Component c is a descendant of one of the * existing roots (or it IS an existing root), we're done. */ for(int i = 0; i < roots.size(); i++) { JComponent root = (JComponent)roots.elementAt(i); for(Component p = c; p != null; p = p.getParent()) { if (p == root) { return; } } } /* Otherwise: if Component c is an ancestor of any of the * existing roots then remove them and add c (the "new root") * to the roots vector. */ for(int i = 0; i < roots.size(); i++) { JComponent root = (JComponent)roots.elementAt(i); for(Component p = root.getParent(); p != null; p = p.getParent()) { if (p == c) { roots.removeElementAt(i--); // !! break; } } } roots.addElement(c); } } /** * We use the ObjectInputStream "registerValidation" * callback to update the UI for the entire tree of components * after they've all been read in. * * @param s the ObjectInputStream from which to read */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); /* If there's no ReadObjectCallback for this stream yet, that is, if * this is the first call to JComponent.readObject() for this * graph of objects, then create a callback and stash it * in the readObjectCallbacks table. Note that the ReadObjectCallback * constructor takes care of calling s.registerValidation(). */ ReadObjectCallback cb = (ReadObjectCallback)(readObjectCallbacks.get(s)); if (cb == null) { try { readObjectCallbacks.put(s, cb = new ReadObjectCallback(s)); } catch (Exception e) { throw new IOException(e.toString()); } } cb.registerComponent(this); if (getToolTipText() != null) { ToolTipManager.sharedInstance().registerComponent(this); } // Read back the client properties. int cpCount = s.readInt(); if (cpCount > 0) { clientProperties = new ArrayTable(); for (int counter = 0; counter < cpCount; counter++) { clientProperties.put(s.readObject(), s.readObject()); } } setWriteObjCounter(this, (byte)0); } /** * Before writing a JComponent to an * ObjectOutputStream we temporarily uninstall its UI. * This is tricky to do because we want to uninstall * the UI before any of the JComponent's children * (or its LayoutManager etc.) are written, * and we don't want to restore the UI until the most derived * JComponent subclass has been been stored. * * @param s the ObjectOutputStream in which to write */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); if (getUIClassID().equals(uiClassID)) { byte count = JComponent.getWriteObjCounter(this); JComponent.setWriteObjCounter(this, --count); if (count == 0 && ui != null) { ui.installUI(this); } } ArrayTable.writeArrayTable(s, clientProperties); } /** * Returns a string representation of this JComponent. * This method * is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not * be null. * * @return a string representation of this JComponent */ protected String paramString() { String preferredSizeString = (isPreferredSizeSet() ? getPreferredSize().toString() : ""); String minimumSizeString = (isMinimumSizeSet() ? getMinimumSize().toString() : ""); String maximumSizeString = (isMaximumSizeSet() ? getMaximumSize().toString() : ""); String borderString = (border != null ? border.toString() : ""); return super.paramString() + ",alignmentX=" + alignmentX + ",alignmentY=" + alignmentY + ",border=" + borderString + ",flags=" + flags + // should beef this up a bit ",maximumSize=" + maximumSizeString + ",minimumSize=" + minimumSizeString + ",preferredSize=" + preferredSizeString; } } Directory: /editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/ =================================================================================== File [added]: MergingOffsetsBagTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/MergingOffsetsBagTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 515 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.Enumeration; import javax.swing.text.AttributeSet; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import junit.framework.AssertionFailedError; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.editor.lib2.highlighting.OffsetGapList; import org.netbeans.spi.editor.highlighting.*; /** * * @author vita */ public class MergingOffsetsBagTest extends NbTestCase { private static final AttributeSet EMPTY = SimpleAttributeSet.EMPTY; private Document doc = new DefaultStyledDocument(); /** Creates a new instance of HighlightSequenceTest */ public MergingOffsetsBagTest(String name) { super(name); } public void testSimple() { OffsetsBag hs = new OffsetsBag(doc, true); assertEquals("Sequence should be empty", 0, hs.getMarks().size()); hs.addHighlight(10, 20, EMPTY); OffsetGapList marks = hs.getMarks(); assertEquals("Sequence should not be empty", 2, marks.size()); assertEquals("Wrong highlight's start offset", 10, marks.get(0).getOffset()); assertEquals("Wrong highlight's end offset", 20, marks.get(1).getOffset()); assertNull("Wrong highlight's end", marks.get(1).getAttributes()); hs.clear(); assertEquals("Sequence was not cleared", 0, hs.getMarks().size()); } public void testAddLeftOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(5, 15, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-B"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 15, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 15, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 20, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", marks.get(2).getAttributes(), "set-A"); assertNull("3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddRightOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(15, 25, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", marks.get(2).getAttributes(), "set-B"); assertNull("3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddLeftMatchBiggerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(10, 15, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A", "set-B"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A"); assertNull("2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddRightMatchBiggerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(15, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertNull("2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddCompleteMatchOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(10, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A", "set-B"); assertNull("1. highlight - wrong end", marks.get(1).getAttributes()); } public void testAddBiggerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(5, 25, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-B"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", marks.get(2).getAttributes(), "set-B"); assertNull("3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddLeftMatchSmallerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(10, 15, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A", "set-B"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A"); assertNull("2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddRightMatchSmallerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(15, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertNull("2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddSmallerOverlap() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(5, 25, attribsA); hs.addHighlight(10, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", marks.get(1).getAttributes(), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", marks.get(0).getAttributes(), "set-A"); assertNull("3. highlight - wrong end", marks.get(3).getAttributes()); } public void testOrdering() { OffsetsBag hs = new OffsetsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("attribute", "value-A"); attribsB.addAttribute("attribute", "value-B"); hs.addHighlight(5, 15, attribsA); hs.addHighlight(10, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong attribs", "value-A", marks.get(0).getAttributes().getAttribute("attribute")); assertEquals("2. highlight - wrong attribs", "value-B", marks.get(1).getAttributes().getAttribute("attribute")); assertEquals("3. highlight - wrong attribs", "value-B", marks.get(2).getAttributes().getAttribute("attribute")); assertNull("3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddMultipleOverlaps() { Object [] src = new Object [] { 5, 10, new String [] { "set-A" }, 15, 20, new String [] { "set-B" }, 25, 30, new String [] { "set-C" }, 0, 40, new String [] { "set-D" }, }; Object [] trg = new Object [] { 0, 5, new String [] { "set-D" }, 5, 10, new String [] { "set-A", "set-D" }, 10, 15, new String [] { "set-D" }, 15, 20, new String [] { "set-B", "set-D" }, 20, 25, new String [] { "set-D" }, 25, 30, new String [] { "set-C", "set-D" }, 30, 40, new String [] { "set-D" }, }; checkMerging(src, trg); } public void testAddMultipleOverlaps2() { Object [] src = new Object [] { 2, 47, new String [] { "set-A" }, 49, 74, new String [] { "set-B" }, 74, 100, new String [] { "set-C" }, 9, 48, new String [] { "set-D" }, 49, 98, new String [] { "set-E" }, 0, 44, new String [] { "set-F" }, 46, 74, new String [] { "set-G" }, 74, 100, new String [] { "set-H" }, }; Object [] trg = new Object [] { 0, 2, new String [] { "set-F" }, 2, 9, new String [] { "set-A", "set-F" }, 9, 44, new String [] { "set-A", "set-D", "set-F" }, 44, 46, new String [] { "set-A", "set-D" }, 46, 47, new String [] { "set-A", "set-D", "set-G" }, 47, 48, new String [] { "set-D", "set-G" }, 48, 49, new String [] { "set-G" }, 49, 74, new String [] { "set-B", "set-E", "set-G" }, 74, 98, new String [] { "set-C", "set-E", "set-H" }, 98, 100, new String [] { "set-C", "set-H" }, }; checkMerging(src, trg); } public void testAddMultipleOverlaps3() { Object [] src = new Object [] { 5, 13, new String [] { "set-A" }, 20, 49, new String [] { "set-B" }, 50, 53, new String [] { "set-C" }, 62, 100, new String [] { "set-D" }, 9, 54, new String [] { "set-E" }, 57, 100, new String [] { "set-F" }, 1, 41, new String [] { "set-G" }, 41, 46, new String [] { "set-H" }, 54, 83, new String [] { "set-I" }, 88, 100, new String [] { "set-J" }, }; Object [] trg = new Object [] { 1, 5, new String [] { "set-G" }, 5, 9, new String [] { "set-A", "set-G" }, 9, 13, new String [] { "set-A", "set-E", "set-G" }, 13, 20, new String [] { "set-E", "set-G" }, 20, 41, new String [] { "set-B", "set-G", "set-E" }, 41, 46, new String [] { "set-B", "set-E", "set-H" }, 46, 49, new String [] { "set-B", "set-E" }, 49, 50, new String [] { "set-E" }, 50, 53, new String [] { "set-C", "set-E" }, 53, 54, new String [] { "set-E" }, 54, 57, new String [] { "set-I" }, 57, 62, new String [] { "set-F", "set-I" }, 62, 83, new String [] { "set-D", "set-F", "set-I" }, 83, 88, new String [] { "set-D", "set-F" }, 88, 100, new String [] { "set-D", "set-F", "set-J" }, }; checkMerging(src, trg); } public void checkMerging(Object [] src, Object [] trg) { OffsetsBag hs = new OffsetsBag(doc, true); for (int i = 0; i < src.length / 3; i++) { SimpleAttributeSet as = new SimpleAttributeSet(); String [] keys = (String []) src[3 * i + 2]; for (int j = 0; j < keys.length; j++) { as.addAttribute(keys[j], Boolean.TRUE); } hs.addHighlight( ((Integer) src[3 * i + 0]).intValue(), ((Integer) src[3 * i + 1]).intValue(), as ); } int lastOffset = Integer.MIN_VALUE; int differentOffsets = 0; for (int i = 0; i < trg.length / 3; i++) { if (lastOffset != ((Integer) trg[3 * i + 0]).intValue()) { differentOffsets++; lastOffset = ((Integer) trg[3 * i + 0]).intValue(); } if (lastOffset != ((Integer) trg[3 * i + 1]).intValue()) { differentOffsets++; lastOffset = ((Integer) trg[3 * i + 1]).intValue(); } } OffsetGapList marks = hs.getMarks(); try { assertEquals("Wrong number of highlights", differentOffsets, marks.size()); int trgIdx = 0; for (int idx = 0; idx < marks.size(); idx++) { if (marks.get(idx).getAttributes() == null) { assertTrue("Mark at index 0 must have attributes", idx > 0); continue; } assertTrue("Too few marks", idx + 1 < marks.size()); assertTrue("Too many marks", trgIdx < trg.length); // Compare one pair assertEquals(trgIdx + ". highlight - wrong start offset", ((Integer) trg[3 * trgIdx + 0]).intValue(), marks.get(idx).getOffset()); assertEquals(trgIdx + ". highlight - wrong end offset", ((Integer) trg[3 * trgIdx + 1]).intValue(), marks.get(idx + 1).getOffset()); assertAttribs(trgIdx + ". highlight - wrong attribs", marks.get(idx).getAttributes(), (String []) trg[3 * trgIdx + 2]); trgIdx++; } assertTrue("Wrong number of marks: marks.size() = " + marks.size() + ", trg.length = " + trg.length, 3 * trgIdx == trg.length); } catch (AssertionFailedError afe) { dumpMarks(marks); System.out.println("Dump through getHighlights {"); HighlightsSequence sequence = hs.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); for ( ; sequence.moveNext(); ) { System.out.println(" <" + sequence.getStartOffset() + ", " + sequence.getEndOffset() + ">"); } System.out.println("} ---- End of Dump through getHighlights ------------------"); throw afe; } } private void assertAttribs(String msg, AttributeSet as, String... keys) { assertEquals(msg, keys.length, as.getAttributeCount()); for (String key : keys) { if (null == as.getAttribute(key)) { fail(msg + " attribute key: " + key); } } } private String dumpHighlight(Position start, Position end, AttributeSet attribs) { StringBuilder sb = new StringBuilder(); sb.append("<"); sb.append(start == null ? " " : start.getOffset()); sb.append(","); sb.append(end == null ? " " : end.getOffset()); sb.append(","); dumpAttributes(sb, attribs); sb.append(">"); return sb.toString(); } private String dumpAttributes(StringBuilder sb, AttributeSet attribs) { if (sb == null) { sb = new StringBuilder(); } if (attribs == null) { sb.append(" "); } else { Enumeration en = attribs.getAttributeNames(); while (en.hasMoreElements()) { Object attrName = en.nextElement(); Object attrValue = attribs.getAttribute(attrName); sb.append("'"); sb.append(attrName.toString()); sb.append("' = '"); sb.append(attrValue == null ? "null" : attrValue.toString()); sb.append("'"); if (en.hasMoreElements()) { sb.append(", "); } } } return sb.toString(); } private void dumpMarks(OffsetGapList marks) { String signature = marks.getClass() + "@" + Integer.toHexString(System.identityHashCode(marks)); System.out.println("Dumping marks from " + signature + " {"); for(OffsetsBag.Mark mark : marks) { System.out.println("<" + mark.getOffset() + ", [" + dumpAttributes(null, mark.getAttributes()) + "]>"); } System.out.println("} ---- End of Dumping marks from " + signature + " --------"); } } File [added]: MergingPositionsBagTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/MergingPositionsBagTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 560 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.Enumeration; import javax.swing.text.AttributeSet; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import junit.framework.AssertionFailedError; import org.netbeans.junit.NbTestCase; import org.netbeans.lib.editor.util.GapList; import org.netbeans.spi.editor.highlighting.*; import org.netbeans.spi.editor.highlighting.performance.SimplePosition; /** * * @author vita */ public class MergingPositionsBagTest extends NbTestCase { private static final AttributeSet EMPTY = SimpleAttributeSet.EMPTY; private Document doc = new DefaultStyledDocument(); /** Creates a new instance of HighlightSequenceTest */ public MergingPositionsBagTest(String name) { super(name); } public void testSimple() { PositionsBag hs = new PositionsBag(doc, true); assertEquals("Sequence should be empty", 0, hs.getMarks().size()); hs.addHighlight(pos(10), pos(20), EMPTY); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Sequence should not be empty", 2, marks.size()); assertEquals("Wrong highlight's start offset", 10, marks.get(0).getOffset()); assertEquals("Wrong highlight's end offset", 20, marks.get(1).getOffset()); assertNull("Wrong highlight's end", attributes.get(1)); hs.clear(); assertEquals("Sequence was not cleared", 0, hs.getMarks().size()); } public void testAddLeftOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(5), pos(15), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-B"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 15, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 15, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 20, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", attributes.get(2), "set-A"); assertNull("3. highlight - wrong end", attributes.get(3)); } public void testAddRightOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(15), pos(25), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", attributes.get(2), "set-B"); assertNull("3. highlight - wrong end", attributes.get(3)); } public void testAddLeftMatchBiggerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(10), pos(15), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A", "set-B"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A"); assertNull("2. highlight - wrong end", attributes.get(2)); } public void testAddRightMatchBiggerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(15), pos(20), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertNull("2. highlight - wrong end", attributes.get(2)); } public void testAddCompleteMatchOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(10), pos(20), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A", "set-B"); assertNull("1. highlight - wrong end", attributes.get(1)); } public void testAddBiggerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(5), pos(25), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-B"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", attributes.get(2), "set-B"); assertNull("3. highlight - wrong end", attributes.get(3)); } public void testAddLeftMatchSmallerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(10), pos(15), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A", "set-B"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A"); assertNull("2. highlight - wrong end", attributes.get(2)); } public void testAddRightMatchSmallerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(15), pos(20), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A"); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertNull("2. highlight - wrong end", attributes.get(2)); } public void testAddSmallerOverlap() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-A", "attribsA"); attribsB.addAttribute("set-B", "attribsB"); hs.addHighlight(pos(5), pos(25), attribsA); hs.addHighlight(pos(10), pos(20), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertAttribs("1. highlight - wrong attribs", attributes.get(0), "set-A"); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertAttribs("2. highlight - wrong attribs", attributes.get(1), "set-A", "set-B"); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertAttribs("3. highlight - wrong attribs", attributes.get(0), "set-A"); assertNull("3. highlight - wrong end", attributes.get(3)); } public void testOrdering() { PositionsBag hs = new PositionsBag(doc, true); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("attribute", "value-A"); attribsB.addAttribute("attribute", "value-B"); hs.addHighlight(pos(5), pos(15), attribsA); hs.addHighlight(pos(10), pos(20), attribsB); GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong attribs", "value-A", attributes.get(0).getAttribute("attribute")); assertEquals("2. highlight - wrong attribs", "value-B", attributes.get(1).getAttribute("attribute")); assertEquals("3. highlight - wrong attribs", "value-B", attributes.get(2).getAttribute("attribute")); assertNull("3. highlight - wrong end", attributes.get(3)); } public void testAddMultipleOverlaps() { Object [] src = new Object [] { 5, 10, new String [] { "set-A" }, 15, 20, new String [] { "set-B" }, 25, 30, new String [] { "set-C" }, 0, 40, new String [] { "set-D" }, }; Object [] trg = new Object [] { 0, 5, new String [] { "set-D" }, 5, 10, new String [] { "set-A", "set-D" }, 10, 15, new String [] { "set-D" }, 15, 20, new String [] { "set-B", "set-D" }, 20, 25, new String [] { "set-D" }, 25, 30, new String [] { "set-C", "set-D" }, 30, 40, new String [] { "set-D" }, }; checkMerging(src, trg, true); } public void testAddMultipleOverlaps2() { Object [] src = new Object [] { 2, 47, new String [] { "set-A" }, 49, 74, new String [] { "set-B" }, 74, 100, new String [] { "set-C" }, 9, 48, new String [] { "set-D" }, 49, 98, new String [] { "set-E" }, 0, 44, new String [] { "set-F" }, 46, 74, new String [] { "set-G" }, 74, 100, new String [] { "set-H" }, }; Object [] trg = new Object [] { 0, 2, new String [] { "set-F" }, 2, 9, new String [] { "set-A", "set-F" }, 9, 44, new String [] { "set-A", "set-D", "set-F" }, 44, 46, new String [] { "set-A", "set-D" }, 46, 47, new String [] { "set-A", "set-D", "set-G" }, 47, 48, new String [] { "set-D", "set-G" }, 48, 49, new String [] { "set-G" }, 49, 74, new String [] { "set-B", "set-E", "set-G" }, 74, 98, new String [] { "set-C", "set-E", "set-H" }, 98, 100, new String [] { "set-C", "set-H" }, }; checkMerging(src, trg, true); } public void testAddMultipleOverlaps3() { Object [] src = new Object [] { 5, 13, new String [] { "set-A" }, 20, 49, new String [] { "set-B" }, 50, 53, new String [] { "set-C" }, 62, 100, new String [] { "set-D" }, 9, 54, new String [] { "set-E" }, 57, 100, new String [] { "set-F" }, 1, 41, new String [] { "set-G" }, 41, 46, new String [] { "set-H" }, 54, 83, new String [] { "set-I" }, 88, 100, new String [] { "set-J" }, }; Object [] trg = new Object [] { 1, 5, new String [] { "set-G" }, 5, 9, new String [] { "set-A", "set-G" }, 9, 13, new String [] { "set-A", "set-E", "set-G" }, 13, 20, new String [] { "set-E", "set-G" }, 20, 41, new String [] { "set-B", "set-G", "set-E" }, 41, 46, new String [] { "set-B", "set-E", "set-H" }, 46, 49, new String [] { "set-B", "set-E" }, 49, 50, new String [] { "set-E" }, 50, 53, new String [] { "set-C", "set-E" }, 53, 54, new String [] { "set-E" }, 54, 57, new String [] { "set-I" }, 57, 62, new String [] { "set-F", "set-I" }, 62, 83, new String [] { "set-D", "set-F", "set-I" }, 83, 88, new String [] { "set-D", "set-F" }, 88, 100, new String [] { "set-D", "set-F", "set-J" }, }; checkMerging(src, trg, true); } public void testAddMultipleOverlaps4() { Object [] src = new Object [] { 4, 51, new String [] { "set-A-0" }, 51, 63, new String [] { "set-A-1" }, 69, 75, new String [] { "set-A-2" }, 83, 85, new String [] { "set-A-3" }, 92, 100, new String [] { "set-A-4" }, 5, 17, new String [] { "set-B-0" }, 23, 37, new String [] { "set-B-1" }, 39, 63, new String [] { "set-B-2" }, 69, 100, new String [] { "set-B-3" }, 2, 28, new String [] { "set-C-0" }, 31, 64, new String [] { "set-C-1" }, 66, 93, new String [] { "set-C-2" }, 95, 100, new String [] { "set-C-3" }, }; Object [] trg = new Object [] { 2, 28, new String [] { "set-C-0" }, 28, 31, new String [] { "set-B-1" }, 31, 64, new String [] { "set-C-1" }, 66, 93, new String [] { "set-C-2" }, 93, 95, new String [] { "set-B-3" }, 95, 100, new String [] { "set-C-3" }, }; checkMerging(src, trg, false); } public void checkMerging(Object [] src, Object [] trg, boolean merge) { PositionsBag hs = new PositionsBag(doc, merge); for (int i = 0; i < src.length / 3; i++) { SimpleAttributeSet as = new SimpleAttributeSet(); String [] keys = (String []) src[3 * i + 2]; for (int j = 0; j < keys.length; j++) { as.addAttribute(keys[j], Boolean.TRUE); } hs.addHighlight( pos(((Integer) src[3 * i + 0]).intValue()), pos(((Integer) src[3 * i + 1]).intValue()), as ); } int lastOffset = Integer.MIN_VALUE; int differentOffsets = 0; for (int i = 0; i < trg.length / 3; i++) { if (lastOffset != ((Integer) trg[3 * i + 0]).intValue()) { differentOffsets++; lastOffset = ((Integer) trg[3 * i + 0]).intValue(); } if (lastOffset != ((Integer) trg[3 * i + 1]).intValue()) { differentOffsets++; lastOffset = ((Integer) trg[3 * i + 1]).intValue(); } } GapList marks = hs.getMarks(); GapList attributes = hs.getAttributes(); try { assertEquals("Wrong number of highlights", differentOffsets, marks.size()); int trgIdx = 0; for (int idx = 0; idx < marks.size(); idx++) { if (attributes.get(idx) == null) { assertTrue("Mark at index 0 must have attributes", idx > 0); continue; } assertTrue("Too few marks", idx + 1 < marks.size()); assertTrue("Too many marks", trgIdx < trg.length); // Compare one pair assertEquals(trgIdx + ". highlight - wrong start offset", ((Integer) trg[3 * trgIdx + 0]).intValue(), marks.get(idx).getOffset()); assertEquals(trgIdx + ". highlight - wrong end offset", ((Integer) trg[3 * trgIdx + 1]).intValue(), marks.get(idx + 1).getOffset()); assertAttribs(trgIdx + ". highlight - wrong attribs", attributes.get(idx), (String []) trg[3 * trgIdx + 2]); trgIdx++; } assertTrue("Wrong number of marks: marks.size() = " + marks.size() + ", trg.length = " + trg.length, 3 * trgIdx == trg.length); } catch (AssertionFailedError afe) { dumpMarks(marks, attributes); System.out.println("Dump through getHighlights {"); HighlightsSequence sequence = hs.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); for ( ; sequence.moveNext(); ) { System.out.println(" <" + sequence.getStartOffset() + ", " + sequence.getEndOffset() + ">"); } System.out.println("} ---- End of Dump through getHighlights ------------------"); throw afe; } } private void assertAttribs(String msg, AttributeSet as, String... keys) { assertEquals(msg, keys.length, as.getAttributeCount()); for (String key : keys) { if (null == as.getAttribute(key)) { fail(msg + " attribute key: " + key); } } } private String dumpHighlight(Position start, Position end, AttributeSet attribs) { StringBuilder sb = new StringBuilder(); sb.append("<"); sb.append(start == null ? " " : start.getOffset()); sb.append(","); sb.append(end == null ? " " : end.getOffset()); sb.append(","); dumpAttributes(sb, attribs); sb.append(">"); return sb.toString(); } private String dumpAttributes(StringBuilder sb, AttributeSet attribs) { if (sb == null) { sb = new StringBuilder(); } if (attribs == null) { sb.append(" "); } else { Enumeration en = attribs.getAttributeNames(); while (en.hasMoreElements()) { Object attrName = en.nextElement(); Object attrValue = attribs.getAttribute(attrName); sb.append("'"); sb.append(attrName.toString()); sb.append("' = '"); sb.append(attrValue == null ? "null" : attrValue.toString()); sb.append("'"); if (en.hasMoreElements()) { sb.append(", "); } } } return sb.toString(); } private void dumpMarks(GapList marks, GapList attributes) { String signature = marks.getClass() + "@" + Integer.toHexString(System.identityHashCode(marks)); System.out.println("Dumping marks from " + signature + " {"); for(int i = 0; i < marks.size(); i++) { System.out.println("<" + marks.get(i).getOffset() + ", [" + dumpAttributes(null, attributes.get(i)) + "]>"); } System.out.println("} ---- End of Dumping marks from " + signature + " --------"); } private Position pos(int offset) { return new SimplePosition(offset); } } File [added]: OffsetsBagTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/OffsetsBagTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 758 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.ConcurrentModificationException; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.editor.lib2.highlighting.OffsetGapList; import org.netbeans.spi.editor.highlighting.*; /** * * @author vita */ public class OffsetsBagTest extends NbTestCase { private static final AttributeSet EMPTY = SimpleAttributeSet.EMPTY; private Document doc = new DefaultStyledDocument(); /** Creates a new instance of HighlightSequenceTest */ public OffsetsBagTest(String name) { super(name); } public void testSimple() { OffsetsBag hs = new OffsetsBag(doc); assertEquals("Sequence should be empty", 0, hs.getMarks().size()); hs.addHighlight(10, 20, EMPTY); OffsetGapList marks = hs.getMarks(); assertEquals("Sequence should not be empty", 2, marks.size()); assertEquals("Wrong highlight's start offset", 10, marks.get(0).getOffset()); assertEquals("Wrong highlight's end offset", 20, marks.get(1).getOffset()); hs.clear(); assertEquals("Sequence was not cleared", 0, hs.getMarks().size()); } public void testAddLeftOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(5, 15, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", marks.get(1).getAttributes().getAttribute("set-name")); assertNull(" 2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddRightOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(15, 25, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 25, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertNull( "2. highlight - wrong end", marks.get(2).getAttributes()); } public void testAddCompleteOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(5, 25, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 25, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); } public void testAddSplit() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 25, attribsA); hs.addHighlight(15, 20, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddAligned() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(20, 30, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 20, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 30, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertNull(" 2. highlight - wrong end", marks.get(2).getAttributes()); hs.addHighlight(0, 10, attribsB); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 30, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsB", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddAligned2() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 40, attribsA); hs.addHighlight(10, 20, attribsB); hs.addHighlight(30, 40, attribsB); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 20, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 30, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 30, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 40, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsB", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); } public void testAddMiddle() { for(int i = 0; i < 10; i++) { addMiddle(i + 1); } } private void addMiddle(int middleMarks) { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); for (int i = 0; i < middleMarks + 1; i++) { hs.addHighlight(10 * i + 10, 10 * i + 20, i % 2 == 0 ? attribsA : attribsB); } hs.addHighlight(15, middleMarks * 10 + 15, attribsC); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights (middleMarks = " + middleMarks + ")", 4, marks.size()); assertEquals("1. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsC", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset (middleMarks = " + middleMarks + ")", (middleMarks + 2) * 10, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs (middleMarks = " + middleMarks + ")", middleMarks % 2 == 0 ? "attribsA" : "attribsB", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end (middleMarks = " + middleMarks + ")", marks.get(3).getAttributes()); } public void testRemoveLeftOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(5, 15, false); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveLeftOverlapClip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(5, 15, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 15, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); } public void testRemoveRightOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(15, 25, false); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveRightOverlapClip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(15, 25, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); } public void testRemoveCompleteOverlap() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(5, 25, false); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveCompleteOverlapClip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(5, 25, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveSplit() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 25, attribsA); hs.removeHighlights(15, 20, false); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveSplitClip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 25, attribsA); hs.removeHighlights(15, 20, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); assertEquals("2. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 2. highlight - wrong end", marks.get(3).getAttributes()); } public void testRemoveAlignedClip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 20, attribsA); hs.removeHighlights(0, 10, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); hs.removeHighlights(20, 30, true); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); } public void testRemoveAligned2Clip() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(10, 40, attribsA); hs.removeHighlights(10, 20, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 20, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 40, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); hs.removeHighlights(30, 40, true); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 20, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 30, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end", marks.get(1).getAttributes()); } public void testRemoveMiddle() { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(10, 20, attribsA); hs.addHighlight(20, 30, attribsB); hs.removeHighlights(15, 25, false); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveMiddleClip() { for(int i = 0; i < 10; i++) { removeMiddleClip(i + 1); } } private void removeMiddleClip(int middleMarks) { OffsetsBag hs = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); for (int i = 0; i < middleMarks + 1; i++) { hs.addHighlight(10 * i + 10, 10 * i + 20, i % 2 == 0 ? attribsA : attribsB); } hs.removeHighlights(15, middleMarks * 10 + 15, true); OffsetGapList marks = hs.getMarks(); assertEquals("Wrong number of highlights (middleMarks = " + middleMarks + ")", 4, marks.size()); assertEquals("1. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertNull(" 1. highlight - wrong end (middleMarks = " + middleMarks + ")", marks.get(1).getAttributes()); assertEquals("2. highlight - wrong start offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("2. highlight - wrong end offset (middleMarks = " + middleMarks + ")", (middleMarks + 2) * 10, marks.get(3).getOffset()); assertEquals("2. highlight - wrong attribs (middleMarks = " + middleMarks + ")", middleMarks % 2 == 0 ? "attribsA" : "attribsB", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 2. highlight - wrong end (middleMarks = " + middleMarks + ")", marks.get(3).getAttributes()); } public void testAddAll() { OffsetsBag hsA = new OffsetsBag(doc); OffsetsBag hsB = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); hsA.addHighlight(0, 30, attribsA); hsA.addHighlight(10, 20, attribsB); OffsetGapList marksA = hsA.getMarks(); hsB.addHighlight(0, 40, attribsC); hsB.addAllHighlights(hsA.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE)); OffsetGapList marksB = hsB.getMarks(); assertEquals("Wrong number of highlights", marksA.size() + 1, marksB.size()); for (int i = 0; i < marksA.size() - 1; i++) { assertEquals(i + ". highlight - wrong start offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); assertEquals(i + ". highlight - wrong end offset", marksA.get(i + 1).getOffset(), marksB.get(i + 1).getOffset()); assertEquals(i + ". highlight - wrong attribs", marksA.get(i).getAttributes().getAttribute("set-name"), marksB.get(i).getAttributes().getAttribute("set-name")); } assertEquals("4. highlight - wrong start offset", 30, marksB.get(3).getOffset()); assertEquals("4. highlight - wrong end offset", 40, marksB.get(4).getOffset()); assertEquals("4. highlight - wrong attribs", "attribsC", marksB.get(3).getAttributes().getAttribute("set-name")); } public void testSet() { OffsetsBag hsA = new OffsetsBag(doc); OffsetsBag hsB = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); hsA.addHighlight(0, 30, attribsA); hsA.addHighlight(10, 20, attribsB); OffsetGapList marksA = hsA.getMarks(); hsB.addHighlight(0, 40, attribsC); hsB.setHighlights(hsA.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE)); OffsetGapList marksB = hsB.getMarks(); assertEquals("Wrong number of highlights", marksA.size(), marksB.size()); for (int i = 0; i < marksA.size(); i++) { assertEquals(i + ". highlight - wrong start offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); assertEquals(i + ". highlight - wrong end offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); AttributeSet attrA = marksA.get(i).getAttributes(); AttributeSet attrB = marksB.get(i).getAttributes(); if (attrA != null && attrB != null) { assertEquals(i + ". highlight - wrong attribs", attrA.getAttribute("set-name"), attrB.getAttribute("set-name")); } else { assertTrue(i + ". highlight - wrong attribs", attrA == null && attrB == null); } } } public void testGetHighlights() { OffsetsBag hs = new OffsetsBag(doc); assertFalse("Sequence should be empty", hs.getHighlights( Integer.MIN_VALUE, Integer.MAX_VALUE).moveNext()); hs.addHighlight(10, 30, EMPTY); { // Do not clip the highlights HighlightsSequence highlights = hs.getHighlights(20, 25); assertTrue("Sequence should not be empty", highlights.moveNext()); assertEquals("Wrong highlight's start offset", 20, highlights.getStartOffset()); assertEquals("Wrong highlight's end offset", 25, highlights.getEndOffset()); assertFalse("There should be no more highlights", highlights.moveNext()); } hs.clear(); assertFalse("Sequence was not cleared", hs.getHighlights( Integer.MIN_VALUE, Integer.MAX_VALUE).moveNext()); } public void testGetHighlights2() { OffsetsBag hb = new OffsetsBag(doc); hb.addHighlight(10, 20, SimpleAttributeSet.EMPTY); HighlightsSequence hs = hb.getHighlights(0, 5); assertFalse("HighlightsSequence should be empty", hs.moveNext()); hs = hb.getHighlights(25, 30); assertFalse("HighlightsSequence should be empty", hs.moveNext()); hs = hb.getHighlights(0, 15); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); hs = hb.getHighlights(12, 22); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); } public void testConcurrentModification() { { OffsetsBag hb = new OffsetsBag(doc); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); // Modify the bag hb.addHighlight(5, 10, EMPTY); try { hs.moveNext(); fail("ConcurrentModificationException has not been thrown from moveNext()"); } catch (ConcurrentModificationException e) { // pass } } { OffsetsBag hb = new OffsetsBag(doc); hb.addHighlight(5, 10, EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(20, 30, EMPTY); try { hs.getStartOffset(); fail("ConcurrentModificationException has not been thrown from getStartPosition()"); } catch (ConcurrentModificationException e) { // pass } } { OffsetsBag hb = new OffsetsBag(doc); hb.addHighlight(5, 10, EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(20, 30, EMPTY); try { hs.getEndOffset(); fail("ConcurrentModificationException has not been thrown from getEndPosition()"); } catch (ConcurrentModificationException e) { // pass } } { OffsetsBag hb = new OffsetsBag(doc); hb.addHighlight(5, 10, EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(20, 30, EMPTY); try { hs.getAttributes(); fail("ConcurrentModificationException has not been thrown from getAttributes()"); } catch (ConcurrentModificationException e) { // pass } } } public void testDocumentChanges() throws BadLocationException { Document doc = new PlainDocument(); doc.insertString(0, "01234567890123456789012345678901234567890123456789", SimpleAttributeSet.EMPTY); OffsetsBag bag = new OffsetsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); bag.addHighlight(0, 30, attribsA); bag.addHighlight(10, 20, attribsB); OffsetGapList marks = bag.getMarks(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 30, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); doc.insertString(12, "----", SimpleAttributeSet.EMPTY); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 24, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 24, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 34, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); doc.remove(1, 5); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 5, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", marks.get(0).getAttributes().getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 5, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 19, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", marks.get(1).getAttributes().getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 19, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 29, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", marks.get(2).getAttributes().getAttribute("set-name")); assertNull(" 3. highlight - wrong end", marks.get(3).getAttributes()); } private void dumpHighlights(HighlightsSequence seq) { System.out.println("Dumping highlights from: " + seq + "{"); while(seq.moveNext()) { System.out.println("<" + seq.getStartOffset() + ", " + seq.getEndOffset() + ", " + seq.getAttributes() + ">"); } System.out.println("} --- End of Dumping highlights from: " + seq + " ---------------------"); } } File [added]: PositionsBagRandomTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/PositionsBagRandomTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 364 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.ArrayList; import java.util.Enumeration; import java.util.Random; import javax.swing.text.AttributeSet; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.editor.highlighting.*; /** * * @author Vita Stejskal */ public class PositionsBagRandomTest extends NbTestCase { private static final int START = 0; private static final int END = 100; private final Random RAND = new Random(); private String [] layerNames; private HighlightsContainer[] containers; public PositionsBagRandomTest(String testName) { super(testName); } protected void setUp() { RAND.setSeed(System.currentTimeMillis()); layerNames = new String [] { "layer-A", "layer-B", "layer-C", }; containers = new HighlightsContainer [layerNames.length]; for (int i = 0; i < layerNames.length; i++) { containers[i] = createRandomBag(layerNames[i]); } } public void testMerging() { HighlightsContainer composite = mergeContainers(true, layerNames, containers); for (int pointer = START; pointer <= END; pointer++) { String failMsg = null; Highlight [] highestPair = new Highlight [] { null, null }; Highlight [] compositePair = new Highlight [] { null, null }; try { highestPair = new Highlight [] { null, null }; compositePair = new Highlight [] { null, null }; // Find all highlights at the position ArrayList leftHighlights = new ArrayList(); ArrayList rightHighlights = new ArrayList(); for (int i = 0; i < containers.length; i++) { Highlight [] containerPair = findPair(pointer, containers[i].getHighlights(START, END)); if (containerPair[0] != null) { leftHighlights.add(containerPair[0].getAttributes()); } if (containerPair[1] != null) { rightHighlights.add(containerPair[1].getAttributes()); } } if (!leftHighlights.isEmpty()) { highestPair[0] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( leftHighlights.toArray(new AttributeSet[leftHighlights.size()]))); } if (!rightHighlights.isEmpty()) { highestPair[1] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( rightHighlights.toArray(new AttributeSet[rightHighlights.size()]))); } // Find the composite container highlight at the position compositePair = findPair(pointer, composite.getHighlights(START, END)); for (int i = 0; i < 2; i++) { if (highestPair[i] != null && compositePair[i] != null) { // Both highlights exist -> check they are the same if (!highestPair[i].getAttributes().isEqual(compositePair[i].getAttributes())) { failMsg = (i == 0 ? "Left" : "Right") + "pair attributes do not match"; } } else if (highestPair[i] != null || compositePair[i] != null) { // Both highlights should be null otherwise they would not match failMsg = (i == 0 ? "Left" : "Right") + " highlight doesn't match"; } } } catch (Throwable e) { failMsg = e.getMessage(); } if (failMsg != null) { dumpAll(pointer, layerNames, containers, composite); // Dump the pair that failed System.out.println("highest pair (pos = " + pointer + ") : " + dumpHighlight(highestPair[0]) + ", " + dumpHighlight(highestPair[1])); System.out.println(" proxy pair (pos = " + pointer + ") : " + dumpHighlight(compositePair[0]) + ", " + dumpHighlight(compositePair[1])); fail(failMsg + " (position = " + pointer + ")"); } } } public void testTrimming() { HighlightsContainer composite = mergeContainers(false, layerNames, containers); for (int pointer = START; pointer <= END; pointer++) { String failMsg = null; Highlight [] highestPair = new Highlight [] { null, null }; Highlight [] compositePair = new Highlight [] { null, null }; try { highestPair = new Highlight [] { null, null }; compositePair = new Highlight [] { null, null }; // Find the highest highlight at the position for (int i = containers.length - 1; i >= 0; i--) { Highlight [] containerPair = findPair(pointer, containers[i].getHighlights(START, END)); if (highestPair[0] == null) { highestPair[0] = containerPair[0]; } if (highestPair[1] == null) { highestPair[1] = containerPair[1]; } } // Find the composite container highlight at the position compositePair = findPair(pointer, composite.getHighlights(START, END)); for (int i = 0; i < 2; i++) { if (highestPair[i] != null && compositePair[i] != null) { // Both highlights exist -> check they are the same if (!highestPair[i].getAttributes().isEqual(compositePair[i].getAttributes())) { failMsg = (i == 0 ? "Left" : "Right") + "pair attributes do not match"; } } else if (highestPair[i] != null || compositePair[i] != null) { // Both highlights should be null otherwise they would not match failMsg = (i == 0 ? "Left" : "Right") + " highlight doesn't match"; } } } catch (Throwable e) { failMsg = e.getMessage(); } if (failMsg != null) { dumpAll(pointer, layerNames, containers, composite); // Dump the pair that failed System.out.println("highest pair (pos = " + pointer + ") : " + dumpHighlight(highestPair[0]) + ", " + dumpHighlight(highestPair[1])); System.out.println(" proxy pair (pos = " + pointer + ") : " + dumpHighlight(compositePair[0]) + ", " + dumpHighlight(compositePair[1])); fail(failMsg + " (position = " + pointer + ")"); } } } private Highlight [] findPair(int offset, HighlightsSequence highlights) { Highlight left = null; Highlight right = null; for ( ; highlights.moveNext(); ) { if (highlights.getStartOffset() == highlights.getEndOffset()) { // ignore empty offsets continue; } if (offset > highlights.getStartOffset() && offset < highlights.getEndOffset()) { left = right = copyCurrentHighlight(highlights); } else if (offset == highlights.getEndOffset()) { left = copyCurrentHighlight(highlights); } else if (offset == highlights.getStartOffset()) { right = copyCurrentHighlight(highlights); } } return new Highlight [] { left, right }; } private Highlight copyCurrentHighlight(HighlightsSequence iterator) { return new Highlight( iterator.getStartOffset(), iterator.getEndOffset(), iterator.getAttributes() ); } private String dumpHighlight(Highlight h) { if (h == null) { return "< , , >"; } else { StringBuilder sb = new StringBuilder(); sb.append("<"); sb.append(h.getStartOffset()); sb.append(","); sb.append(h.getEndOffset()); sb.append(","); Enumeration en = h.getAttributes().getAttributeNames(); while (en.hasMoreElements()) { Object attrName = en.nextElement(); Object attrValue = h.getAttributes().getAttribute(attrName); sb.append("'"); sb.append(attrName.toString()); sb.append("' = '"); sb.append(attrValue == null ? "null" : attrValue.toString()); sb.append("'"); if (en.hasMoreElements()) { sb.append(", "); } } sb.append(">"); return sb.toString(); } } private void dumpAll(int position, String [] layerNames, HighlightsContainer[] containers, HighlightsContainer composite) { // Dump the layers System.out.println("Dumping containers:"); for (int i = 0; i < containers.length; i++) { System.out.println(" containers[" + i + "] " + layerNames[i] + " {"); for (HighlightsSequence highlights = containers[i].getHighlights(START, END); highlights.moveNext(); ) { Highlight h = copyCurrentHighlight(highlights); System.out.println(" " + dumpHighlight(h)); } System.out.println(" } End of containers[" + i + "] " + layerNames[i] + " -------------------------"); } System.out.println("Dumping composite container: {"); for (HighlightsSequence proxyHighlights = composite.getHighlights(START, END); proxyHighlights.moveNext(); ) { Highlight h = copyCurrentHighlight(proxyHighlights); System.out.println(" " + dumpHighlight(h)); } System.out.println("} End of composite container -----------------------"); } private PositionsBag createRandomBag(String bagId) { PositionsBag bag = new PositionsBag(new PlainDocument(), false); int attrIdx = 0; int startOffset = START; int endOffset = END; int maxGapSize = Math.max((int) (endOffset - startOffset) / 10, 1); int maxHighlightSize = Math.max((int) (endOffset - startOffset) / 2, 1); for (int pointer = startOffset + RAND.nextInt(maxGapSize); pointer <= endOffset; ) { int highlightSize = RAND.nextInt(maxHighlightSize); SimpleAttributeSet attributes = new SimpleAttributeSet(); attributes.addAttribute("AttrName-" + bagId + "-" + attrIdx, "AttrValue"); attrIdx++; if (pointer + highlightSize < endOffset) { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(pointer + highlightSize), attributes); } else { bag.addHighlight( new SimplePosition(pointer), new SimplePosition(endOffset), attributes); } // move the pointer pointer += highlightSize + RAND.nextInt(maxGapSize); } return bag; } private PositionsBag mergeContainers(boolean merge, String [] layerNames, HighlightsContainer[] containers) { PositionsBag bag = new PositionsBag(new PlainDocument(), merge); for (int i = 0; i < containers.length; i++) { HighlightsSequence layerHighlights = containers[i].getHighlights(START, END); for ( ; layerHighlights.moveNext(); ) { bag.addHighlight( new SimplePosition(layerHighlights.getStartOffset()), new SimplePosition(layerHighlights.getEndOffset()), layerHighlights.getAttributes()); } } return bag; } private static final class Highlight { private int startOffset; private int endOffset; private AttributeSet attributes; public Highlight(int startOffset, int endOffset, AttributeSet attributes) { this.startOffset = startOffset; this.endOffset = endOffset; this.attributes = attributes; } public int getStartOffset() { return startOffset; } public void setStartOffset(int startOffset) { this.startOffset = startOffset; } public int getEndOffset() { return endOffset; } public void setEndOffset(int endOffset) { this.endOffset = endOffset; } public AttributeSet getAttributes() { return attributes; } public void setAttributes(AttributeSet attributes) { this.attributes = attributes; } } // End of H class private static final class SimplePosition implements Position { private int offset; public SimplePosition(int offset) { this.offset = offset; } public int getOffset() { return offset; } } // End of SimplePosition class } File [added]: PositionsBagTest.java Url: http://editor.netbeans.org/source/browse/editor/lib2/test/unit/src/org/netbeans/spi/editor/highlighting/support/PositionsBagTest.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 782 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.spi.editor.highlighting.support; import java.util.ConcurrentModificationException; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; import org.netbeans.junit.NbTestCase; import org.netbeans.lib.editor.util.GapList; import org.netbeans.spi.editor.highlighting.*; import org.netbeans.spi.editor.highlighting.performance.SimplePosition; /** * * @author vita */ public class PositionsBagTest extends NbTestCase { private static final AttributeSet EMPTY = SimpleAttributeSet.EMPTY; private Document doc = new DefaultStyledDocument(); /** Creates a new instance of HighlightSequenceTest */ public PositionsBagTest(String name) { super(name); } public void testSimple() { PositionsBag hs = new PositionsBag(doc); assertEquals("Sequence should be empty", 0, hs.getMarks().size()); hs.addHighlight(pos(10), pos(20), EMPTY); GapList marks = hs.getMarks(); assertEquals("Sequence should not be empty", 2, marks.size()); assertEquals("Wrong highlight's start offset", 10, marks.get(0).getOffset()); assertEquals("Wrong highlight's end offset", 20, marks.get(1).getOffset()); hs.clear(); assertEquals("Sequence was not cleared", 0, hs.getMarks().size()); } public void testAddLeftOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(5), pos(15), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", atttributes.get(1).getAttribute("set-name")); assertNull(" 2. highlight - wrong end", atttributes.get(2)); } public void testAddRightOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(15), pos(25), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 25, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertNull( "2. highlight - wrong end", atttributes.get(2)); } public void testAddCompleteOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(5), pos(25), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 5, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 25, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); } public void testAddSplit() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(25), attribsA); hs.addHighlight(pos(15), pos(20), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); } public void testAddAligned() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(20), pos(30), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 3, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 20, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 30, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertNull(" 2. highlight - wrong end", atttributes.get(2)); hs.addHighlight(pos(0), pos(10), attribsB); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 30, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsB", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); } public void testAddAligned2() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(40), attribsA); hs.addHighlight(pos(10), pos(20), attribsB); hs.addHighlight(pos(30), pos(40), attribsB); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsB", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 20, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 30, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 30, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 40, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsB", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); } public void testAddMiddle() { for(int i = 0; i < 10; i++) { addMiddle(i + 1); } } private void addMiddle(int middleMarks) { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); for (int i = 0; i < middleMarks + 1; i++) { hs.addHighlight(pos(10 * i + 10), pos(10 * i + 20), i % 2 == 0 ? attribsA : attribsB); } hs.addHighlight(pos(15), pos(middleMarks * 10 + 15), attribsC); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights (middleMarks = " + middleMarks + ")", 4, marks.size()); assertEquals("1. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsC", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset (middleMarks = " + middleMarks + ")", (middleMarks + 2) * 10, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs (middleMarks = " + middleMarks + ")", middleMarks % 2 == 0 ? "attribsA" : "attribsB", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end (middleMarks = " + middleMarks + ")", atttributes.get(3)); } public void testRemoveLeftOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(5, 15); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveLeftOverlapClip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(pos(5), pos(15), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 15, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); } public void testRemoveRightOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(15, 25); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveRightOverlapClip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(pos(15), pos(25), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); } public void testRemoveCompleteOverlap() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(5, 25); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveCompleteOverlapClip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(pos(5), pos(25), true); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveSplit() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(25), attribsA); hs.removeHighlights(15, 20); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveSplitClip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(25), attribsA); hs.removeHighlights(pos(15), pos(20), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); assertEquals("2. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong end offset", 25, marks.get(3).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsA", atttributes.get(2).getAttribute("set-name")); assertNull(" 2. highlight - wrong end", atttributes.get(3)); } public void testRemoveAlignedClip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(20), attribsA); hs.removeHighlights(pos(0), pos(10), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); hs.removeHighlights(pos(20), pos(30), true); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 20, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); } public void testRemoveAligned2Clip() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); hs.addHighlight(pos(10), pos(40), attribsA); hs.removeHighlights(pos(10), pos(20), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 20, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 40, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); hs.removeHighlights(pos(30), pos(40), true); assertEquals("Wrong number of highlights", 2, marks.size()); assertEquals("1. highlight - wrong start offset", 20, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 30, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end", atttributes.get(1)); } public void testRemoveMiddle() { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); hs.addHighlight(pos(10), pos(20), attribsA); hs.addHighlight(pos(20), pos(30), attribsB); hs.removeHighlights(15, 25); GapList marks = hs.getMarks(); assertEquals("Wrong number of highlights", 0, marks.size()); } public void testRemoveMiddleClip() { for(int i = 0; i < 10; i++) { removeMiddleClip(i + 1); } } private void removeMiddleClip(int middleMarks) { PositionsBag hs = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); for (int i = 0; i < middleMarks + 1; i++) { hs.addHighlight(pos(10 * i + 10), pos(10 * i + 20), i % 2 == 0 ? attribsA : attribsB); } hs.removeHighlights(pos(15), pos(middleMarks * 10 + 15), true); GapList marks = hs.getMarks(); GapList atttributes = hs.getAttributes(); assertEquals("Wrong number of highlights (middleMarks = " + middleMarks + ")", 4, marks.size()); assertEquals("1. highlight - wrong start offset (middleMarks = " + middleMarks + ")", 10, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset (middleMarks = " + middleMarks + ")", 15, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs (middleMarks = " + middleMarks + ")", "attribsA", atttributes.get(0).getAttribute("set-name")); assertNull(" 1. highlight - wrong end (middleMarks = " + middleMarks + ")", atttributes.get(1)); assertEquals("2. highlight - wrong start offset (middleMarks = " + middleMarks + ")", middleMarks * 10 + 15, marks.get(2).getOffset()); assertEquals("2. highlight - wrong end offset (middleMarks = " + middleMarks + ")", (middleMarks + 2) * 10, marks.get(3).getOffset()); assertEquals("2. highlight - wrong attribs (middleMarks = " + middleMarks + ")", middleMarks % 2 == 0 ? "attribsA" : "attribsB", atttributes.get(2).getAttribute("set-name")); assertNull(" 2. highlight - wrong end (middleMarks = " + middleMarks + ")", atttributes.get(3)); } public void testAddAll() { PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); hsA.addHighlight(pos(0), pos(30), attribsA); hsA.addHighlight(pos(10), pos(20), attribsB); GapList marksA = hsA.getMarks(); GapList atttributesA = hsA.getAttributes(); hsB.addHighlight(pos(0), pos(40), attribsC); hsB.addAllHighlights(hsA); GapList marksB = hsB.getMarks(); GapList atttributesB = hsB.getAttributes(); assertEquals("Wrong number of highlights", marksA.size() + 1, marksB.size()); for (int i = 0; i < marksA.size() - 1; i++) { assertEquals(i + ". highlight - wrong start offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); assertEquals(i + ". highlight - wrong end offset", marksA.get(i + 1).getOffset(), marksB.get(i + 1).getOffset()); assertEquals(i + ". highlight - wrong attribs", atttributesA.get(i).getAttribute("set-name"), atttributesB.get(i).getAttribute("set-name")); } assertEquals("4. highlight - wrong start offset", 30, marksB.get(3).getOffset()); assertEquals("4. highlight - wrong end offset", 40, marksB.get(4).getOffset()); assertEquals("4. highlight - wrong attribs", "attribsC", atttributesB.get(3).getAttribute("set-name")); } public void testSet() { PositionsBag hsA = new PositionsBag(doc); PositionsBag hsB = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); SimpleAttributeSet attribsC = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); attribsC.addAttribute("set-name", "attribsC"); hsA.addHighlight(pos(0), pos(30), attribsA); hsA.addHighlight(pos(10), pos(20), attribsB); GapList marksA = hsA.getMarks(); GapList atttributesA = hsA.getAttributes(); hsB.addHighlight(pos(0), pos(40), attribsC); hsB.setHighlights(hsA); GapList marksB = hsB.getMarks(); GapList atttributesB = hsB.getAttributes(); assertEquals("Wrong number of highlights", marksA.size(), marksB.size()); for (int i = 0; i < marksA.size(); i++) { assertEquals(i + ". highlight - wrong start offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); assertEquals(i + ". highlight - wrong end offset", marksA.get(i).getOffset(), marksB.get(i).getOffset()); AttributeSet attrA = atttributesA.get(i); AttributeSet attrB = atttributesB.get(i); if (attrA != null && attrB != null) { assertEquals(i + ". highlight - wrong attribs", attrA.getAttribute("set-name"), attrB.getAttribute("set-name")); } else { assertTrue(i + ". highlight - wrong attribs", attrA == null && attrB == null); } } } public void testGetHighlights() { PositionsBag hs = new PositionsBag(doc); assertFalse("Sequence should be empty", hs.getHighlights( Integer.MIN_VALUE, Integer.MAX_VALUE).moveNext()); hs.addHighlight(pos(10), pos(30), EMPTY); { // Do not clip the highlights HighlightsSequence highlights = hs.getHighlights(20, 25); assertTrue("Sequence should not be empty", highlights.moveNext()); assertEquals("Wrong highlight's start offset", 20, highlights.getStartOffset()); assertEquals("Wrong highlight's end offset", 25, highlights.getEndOffset()); assertFalse("There should be no more highlights", highlights.moveNext()); } hs.clear(); assertFalse("Sequence was not cleared", hs.getHighlights( Integer.MIN_VALUE, Integer.MAX_VALUE).moveNext()); } public void testGetHighlights2() { PositionsBag hb = new PositionsBag(doc); hb.addHighlight(pos(10), pos(20), SimpleAttributeSet.EMPTY); HighlightsSequence hs = hb.getHighlights(0, 5); assertFalse("HighlightsSequence should be empty", hs.moveNext()); hs = hb.getHighlights(25, 30); assertFalse("HighlightsSequence should be empty", hs.moveNext()); hs = hb.getHighlights(0, 15); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); hs = hb.getHighlights(12, 22); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("HighlightsSequence should not be empty", hs.moveNext()); assertFalse("Too many highlights in the sequence", hs.moveNext()); } public void testConcurrentModification() { { PositionsBag hb = new PositionsBag(doc); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); // Modify the bag hb.addHighlight(pos(5), pos(10), EMPTY); try { hs.moveNext(); fail("ConcurrentModificationException has not been thrown from moveNext()"); } catch (ConcurrentModificationException e) { // pass } } { PositionsBag hb = new PositionsBag(doc); hb.addHighlight(pos(5), pos(10), EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(pos(20), pos(30), EMPTY); try { hs.getStartOffset(); fail("ConcurrentModificationException has not been thrown from getStartPosition()"); } catch (ConcurrentModificationException e) { // pass } } { PositionsBag hb = new PositionsBag(doc); hb.addHighlight(pos(5), pos(10), EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(pos(20), pos(30), EMPTY); try { hs.getEndOffset(); fail("ConcurrentModificationException has not been thrown from getEndPosition()"); } catch (ConcurrentModificationException e) { // pass } } { PositionsBag hb = new PositionsBag(doc); hb.addHighlight(pos(5), pos(10), EMPTY); HighlightsSequence hs = hb.getHighlights(Integer.MIN_VALUE, Integer.MAX_VALUE); assertTrue("Sequence should not be empty", hs.moveNext()); // Modify the bag hb.addHighlight(pos(20), pos(30), EMPTY); try { hs.getAttributes(); fail("ConcurrentModificationException has not been thrown from getAttributes()"); } catch (ConcurrentModificationException e) { // pass } } } public void testDocumentChanges() throws BadLocationException { Document doc = new PlainDocument(); doc.insertString(0, "01234567890123456789012345678901234567890123456789", SimpleAttributeSet.EMPTY); PositionsBag bag = new PositionsBag(doc); SimpleAttributeSet attribsA = new SimpleAttributeSet(); SimpleAttributeSet attribsB = new SimpleAttributeSet(); attribsA.addAttribute("set-name", "attribsA"); attribsB.addAttribute("set-name", "attribsB"); bag.addHighlight(doc.createPosition(0), doc.createPosition(30), attribsA); bag.addHighlight(doc.createPosition(10), doc.createPosition(20), attribsB); GapList marks = bag.getMarks(); GapList atttributes = bag.getAttributes(); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 20, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 20, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 30, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); doc.insertString(12, "----", SimpleAttributeSet.EMPTY); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 10, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 10, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 24, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 24, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 34, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); doc.remove(1, 5); assertEquals("Wrong number of highlights", 4, marks.size()); assertEquals("1. highlight - wrong start offset", 0, marks.get(0).getOffset()); assertEquals("1. highlight - wrong end offset", 5, marks.get(1).getOffset()); assertEquals("1. highlight - wrong attribs", "attribsA", atttributes.get(0).getAttribute("set-name")); assertEquals("2. highlight - wrong start offset", 5, marks.get(1).getOffset()); assertEquals("2. highlight - wrong end offset", 19, marks.get(2).getOffset()); assertEquals("2. highlight - wrong attribs", "attribsB", atttributes.get(1).getAttribute("set-name")); assertEquals("3. highlight - wrong start offset", 19, marks.get(2).getOffset()); assertEquals("3. highlight - wrong end offset", 29, marks.get(3).getOffset()); assertEquals("3. highlight - wrong attribs", "attribsA", atttributes.get(2).getAttribute("set-name")); assertNull(" 3. highlight - wrong end", atttributes.get(3)); } private void dumpHighlights(HighlightsSequence seq) { System.out.println("Dumping highlights from: " + seq + "{"); while(seq.moveNext()) { System.out.println("<" + seq.getStartOffset() + ", " + seq.getEndOffset() + ", " + seq.getAttributes() + ">"); } System.out.println("} --- End of Dumping highlights from: " + seq + " ---------------------"); } private Position pos(int offset) { return new SimplePosition(offset); } } Directory: /editor/libsrc/org/netbeans/editor/ ============================================== File [changed]: ActionFactory.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ActionFactory.java?r1=1.79&r2=1.80 Delta lines: +8 -10 -------------------- --- ActionFactory.java 22 Nov 2006 18:09:40 -0000 1.79 +++ ActionFactory.java 19 Jan 2007 05:21:30 -0000 1.80 @@ -45,14 +45,12 @@ import javax.swing.JCheckBoxMenuItem; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; -import java.util.ArrayList; -import java.util.List; import javax.swing.text.AbstractDocument; import javax.swing.text.View; -import org.netbeans.editor.DrawEngineDocView; import org.netbeans.api.editor.fold.Fold; import org.netbeans.api.editor.fold.FoldHierarchy; import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.modules.editor.lib2.search.EditorFindSupport; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.openide.util.NbBundle; @@ -637,7 +635,7 @@ public void actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { - FindSupport.getFindSupport().find(null, false); + EditorFindSupport.getInstance().find(null, false); } } } @@ -655,7 +653,7 @@ public void actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { - FindSupport.getFindSupport().find(null, true); + EditorFindSupport.getInstance().find(null, true); } } } @@ -675,14 +673,14 @@ public void actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { - FindSupport findSupport = FindSupport.getFindSupport(); + EditorFindSupport findSupport = EditorFindSupport.getInstance(); Caret caret = target.getCaret(); int dotPos = caret.getDot(); HashMap props = new HashMap(findSupport.getFindProperties()); String searchWord = null; boolean revert = false; Boolean originalValue = null; - Map revertMap = (Map)props.get(FindSupport.REVERT_MAP); + Map revertMap = (Map)props.get(EditorFindSupport.REVERT_MAP); Boolean revertValue = revertMap != null ? (Boolean)revertMap.get(SettingsNames.FIND_WHOLE_WORDS) : null; if (caret.isSelectionVisible()) { // valid selection @@ -718,7 +716,7 @@ if (revert){ revertMap = new HashMap(); revertMap.put(SettingsNames.FIND_WHOLE_WORDS, originalValue != null ? originalValue : Boolean.FALSE); - props.put(FindSupport.REVERT_MAP, revertMap); + props.put(EditorFindSupport.REVERT_MAP, revertMap); } findSupport.putFindProperties(props); @@ -740,14 +738,14 @@ public void actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { - Boolean cur = (Boolean)FindSupport.getFindSupport().getFindProperty( + Boolean cur = (Boolean)EditorFindSupport.getInstance().getFindProperty( SettingsNames.FIND_HIGHLIGHT_SEARCH); if (cur == null || cur.booleanValue() == false) { cur = Boolean.TRUE; } else { cur = Boolean.FALSE; } - FindSupport.getFindSupport().putFindProperty( + EditorFindSupport.getInstance().putFindProperty( SettingsNames.FIND_HIGHLIGHT_SEARCH, cur); } } File [changed]: BaseCaret.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/BaseCaret.java?r1=1.126&r2=1.127 Delta lines: +2 -22 -------------------- --- BaseCaret.java 23 Oct 2006 12:46:15 -0000 1.126 +++ BaseCaret.java 19 Jan 2007 05:21:30 -0000 1.127 @@ -121,11 +121,10 @@ Point magicCaretPosition; /** Draw mark designating the position of the caret. */ - MarkFactory.DrawMark caretMark = new MarkFactory.CaretMark(); + MarkFactory.ContextMark caretMark = new MarkFactory.ContextMark(Position.Bias.Forward, false); /** Draw mark that supports caret mark in creating selection */ - MarkFactory.DrawMark selectionMark = new MarkFactory.DrawMark( - DrawLayerFactory.CARET_LAYER_NAME, null); + MarkFactory.ContextMark selectionMark = new MarkFactory.ContextMark(Position.Bias.Forward, false); /** Is the caret visible */ boolean caretVisible; @@ -337,10 +336,6 @@ component.addMouseMotionListener(this); EditorUI editorUI = Utilities.getEditorUI(component); - editorUI.addLayer(new DrawLayerFactory.CaretLayer(), - DrawLayerFactory.CARET_LAYER_VISIBILITY); - caretMark.setEditorUI(editorUI); - selectionMark.setEditorUI(editorUI); editorUI.addPropertyChangeListener( this ); FoldHierarchy hierarchy = FoldHierarchy.get(c); @@ -369,8 +364,6 @@ } } - Utilities.getEditorUI(c).removeLayer(DrawLayerFactory.CARET_LAYER_NAME); - c.removeMouseMotionListener(this); c.removeMouseListener(this); c.removeFocusListener(listenerImpl); @@ -771,16 +764,6 @@ JTextComponent c = component; if (c != null) { selectionVisible = v; - if (selectionVisible) { - int caretPos = getDot(); - int selPos = getMark(); - boolean selMarkFirst = (selPos < caretPos); - selectionMark.activateLayer = selMarkFirst; - caretMark.activateLayer = !selMarkFirst && !(selPos == caretPos); - } else { // make selection invisible - caretMark.activateLayer = false; - selectionMark.activateLayer = false; - } // repaint the block BaseTextUI ui = (BaseTextUI)c.getUI(); @@ -1017,9 +1000,6 @@ Utilities.moveMark(doc, caretMark, offset); if (selectionVisible) { // selection already visible - boolean selMarkFirst = (selPos < offset); - selectionMark.activateLayer = selMarkFirst; - caretMark.activateLayer = !selMarkFirst && !(selPos == offset); Utilities.getEditorUI(c).repaintBlock(oldCaretPos, offset); if (selPos == offset) { // same positions -> invisible selection setSelectionVisible(false); File [changed]: BaseDocument.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/BaseDocument.java?r1=1.136&r2=1.137 Delta lines: +40 -25 --------------------- --- BaseDocument.java 2 Jan 2007 13:42:25 -0000 1.136 +++ BaseDocument.java 19 Jan 2007 05:21:30 -0000 1.137 @@ -33,6 +33,7 @@ import java.beans.PropertyChangeEvent; import java.util.EventListener; import java.util.HashMap; +import java.util.logging.Logger; import javax.swing.event.DocumentListener; import javax.swing.event.UndoableEditListener; import javax.swing.text.BadLocationException; @@ -42,7 +43,6 @@ import javax.swing.text.AttributeSet; import javax.swing.text.AbstractDocument; import javax.swing.text.StyleConstants; -import javax.swing.event.EventListenerList; import javax.swing.event.DocumentEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.text.Segment; @@ -62,6 +62,8 @@ public class BaseDocument extends AbstractDocument implements SettingsChangeListener, AtomicLockDocument { + private static final Logger LOG = Logger.getLogger(BaseDocument.class.getName()); + /** Registry identification property */ public static final String ID_PROP = "id"; // NOI18N @@ -297,13 +299,6 @@ // Line separators default to platform ones putProperty(READ_LINE_SEPARATOR_PROP, Analyzer.getPlatformLS()); - // Add document draw-layers - addLayer(new DrawLayerFactory.SyntaxLayer(), - DrawLayerFactory.SYNTAX_LAYER_VISIBILITY); - - addLayer(new DrawLayerFactory.HighlightSearchLayer(), - DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_VISIBILITY); - // Additional initialization of the document through the kit BaseKit kit = BaseKit.getKit(kitClass); if (kit != null) { @@ -338,17 +333,6 @@ putProperty(STRING_FINDER_PROP, null); putProperty(STRING_BWD_FINDER_PROP, null); putProperty(BLOCKS_FINDER_PROP, null); - - DrawLayerFactory.HighlightSearchLayer hsl - = (DrawLayerFactory.HighlightSearchLayer)findLayer( - DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_NAME); - - Boolean b = (Boolean)FindSupport.getFindSupport().getPropertyNoInit( - SettingsNames.FIND_HIGHLIGHT_SEARCH); - hsl.setEnabled((b != null) ? b.booleanValue() : false); - - fireChangedUpdate(createDocumentEvent(0, getLength(), - DocumentEvent.EventType.CHANGE)); // refresh whole document } /** Called when settings were changed. The method is called @@ -897,6 +881,11 @@ protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { super.insertUpdate(chng, attr); + // Store modification text as an event's property + org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng); + org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(chng, String.class, + ((BaseDocumentEvent)chng).getText()); + MarksStorageUndo marksStorageUndo = new MarksStorageUndo(chng); marksStorageUndo.updateMarksStorage(); chng.addEdit(marksStorageUndo); // fix compatible marks @@ -926,6 +915,18 @@ protected void removeUpdate(DefaultDocumentEvent chng) { super.removeUpdate(chng); + // Store modification text as an event's property + org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng); + String removedText; + try { + removedText = getText(chng.getOffset(), chng.getLength()); + } catch (BadLocationException e) { + // Ignore + removedText = null; + } + org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(chng, String.class, + removedText); + // Remember the line changes here but add them to chng during postRemoveUpdate() removeUpdateLineUndo = lineRootElement.removeUpdate(chng.getOffset(), chng.getLength()); @@ -1581,13 +1582,15 @@ } public void addDocumentListener(DocumentListener listener) { - org.netbeans.lib.editor.util.swing.DocumentUtilities.addDocumentListener( - this, listener, DocumentListenerPriority.DEFAULT); + if (!org.netbeans.lib.editor.util.swing.DocumentUtilities.addPriorityDocumentListener( + this, listener, DocumentListenerPriority.DEFAULT)) + super.addDocumentListener(listener); } public void removeDocumentListener(DocumentListener listener) { - org.netbeans.lib.editor.util.swing.DocumentUtilities.removeDocumentListener( - this, listener, DocumentListenerPriority.DEFAULT); + if (!org.netbeans.lib.editor.util.swing.DocumentUtilities.removePriorityDocumentListener( + this, listener, DocumentListenerPriority.DEFAULT)) + super.removeDocumentListener(listener); } protected BaseDocumentEvent createDocumentEvent(int pos, int length, @@ -1622,11 +1625,23 @@ return modified; } - /** Get the layer with the specified name */ + /** + * Get the layer with the specified name. Using of DrawLayers + * has been deprecated. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public DrawLayer findLayer(String layerName) { return drawLayerList.findLayer(layerName); } + /** + * Using of DrawLayers has been deprecated. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public boolean addLayer(DrawLayer layer, int visibility) { if (drawLayerList.add(layer, visibility)) { BaseDocumentEvent evt = createDocumentEvent(0, 0, DocumentEvent.EventType.CHANGE); File [changed]: BaseTextUI.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/BaseTextUI.java?r1=1.81&r2=1.82 Delta lines: +4 -3 ------------------- --- BaseTextUI.java 30 Jun 2006 19:17:47 -0000 1.81 +++ BaseTextUI.java 19 Jan 2007 05:21:30 -0000 1.82 @@ -23,20 +23,19 @@ import java.awt.event.ActionEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Iterator; import javax.swing.text.*; import javax.swing.event.DocumentListener; import javax.swing.event.DocumentEvent; import javax.swing.plaf.TextUI; -import javax.swing.plaf.ComponentUI; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.SwingUtilities; import javax.swing.Action; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicTextUI; -import org.netbeans.editor.SettingsChangeListener; import org.netbeans.editor.view.spi.LockView; /** @@ -327,6 +326,7 @@ * We are looking for document changes on the component. */ public void propertyChange(PropertyChangeEvent evt) { + JTextComponent comp = (JTextComponent)evt.getSource(); String propName = evt.getPropertyName(); if ("document".equals(propName)) { // NOI18N BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument) @@ -334,6 +334,7 @@ if (oldDoc != null) { oldDoc.removeDocumentListener(this); + oldDoc.putProperty("documentInstalledInComponentWeakRef", null); //NOI18N } BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument) @@ -341,10 +342,10 @@ if (newDoc != null) { newDoc.addDocumentListener(this); + newDoc.putProperty("documentInstalledInComponentWeakRef", new WeakReference(comp)); //NOI18N Registry.activate(newDoc); // Activate the new document } } else if ("ancestor".equals(propName)) { // NOI18N - JTextComponent comp = (JTextComponent)evt.getSource(); if (comp.isDisplayable() && editorUI != null && editorUI.hasExtComponent()) { // #41209: In case extComponent was retrieved set the ancestorOverride // to true and expect that the editor kit that installed File [changed]: DialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/DialogSupport.java?r1=1.2&r2=1.3 Delta lines: +19 -91 --------------------- --- DialogSupport.java 30 Jun 2006 19:17:48 -0000 1.2 +++ DialogSupport.java 19 Jan 2007 05:21:30 -0000 1.3 @@ -19,14 +19,9 @@ package org.netbeans.editor; -import java.awt.Insets; import java.awt.Dialog; import java.awt.event.*; -import java.awt.LayoutManager; -import java.awt.BorderLayout; -import java.awt.GridLayout; import javax.swing.*; -import javax.swing.border.EmptyBorder; /** * DialogSupport is factory based class for creating dialogs of certain @@ -36,11 +31,11 @@ * * @author pnejedly * @version 1.0 + * @deprecated See org.openide.spi.editor.lib2.DialogFactory. DialogSupport has + * no public replacement. */ public class DialogSupport { - private static DialogFactory factory; - /** Noone needs to instantiate the dialog support */ private DialogSupport() { } @@ -65,11 +60,9 @@ JButton[] buttons, boolean sidebuttons, int defaultIndex, int cancelIndex, ActionListener listener ) { - if( factory == null ) { - factory = new DefaultDialogFactory(); - } - return factory.createDialog(title, panel, modal, buttons, sidebuttons, - defaultIndex, cancelIndex, listener ); + return org.netbeans.modules.editor.lib2.DialogSupport.getInstance().createDialog( + title, panel, modal, buttons, sidebuttons, defaultIndex, cancelIndex, listener + ); } /** The method for setting custom factory for creating dialogs via @@ -83,10 +76,9 @@ * @see DialogSupport.DefaultDialogFactory */ public static void setDialogFactory( DialogFactory factory ) { - DialogSupport.factory = factory; + org.netbeans.modules.editor.lib2.DialogSupport.getInstance().setExternalDialogFactory(new Wrapper(factory)); } - /** * DialogFactory implementation is a class responsible for providing * proper implementation of Dialog containing required widgets. @@ -114,88 +106,24 @@ public Dialog createDialog( String title, JPanel panel, boolean modal, JButton[] buttons, boolean sidebuttons, int defaultIndex, int cancelIndex, ActionListener listener ); - } - - - /** The DialogFactory that will be used to create Dialogs if no other - * DialogFactory is set to DialogSupport. - */ - private static class DefaultDialogFactory extends WindowAdapter implements DialogFactory, ActionListener { - - private JButton cancelButton; - - /** Create a panel with buttons that will be placed according - * to the required alignment */ - JPanel createButtonPanel( JButton[] buttons, boolean sidebuttons ) { - int count = buttons.length; - - JPanel outerPanel = new JPanel( new BorderLayout() ); - outerPanel.setBorder( new EmptyBorder( new Insets( - sidebuttons ? 5 : 0, sidebuttons ? 0 : 5, 5, 5 ) ) ); + } // End of DialogFactory interface - LayoutManager lm = new GridLayout( // GridLayout makes equal cells - sidebuttons ? count : 1, sidebuttons ? 1 : count, 5, 5 ); + private static final class Wrapper implements org.netbeans.spi.editor.DialogFactory { - JPanel innerPanel = new JPanel( lm ); + private DialogFactory origFactory; - for( int i = 0; i < count; i++ ) innerPanel.add( buttons[i] ); - - outerPanel.add( innerPanel, - sidebuttons ? BorderLayout.NORTH : BorderLayout.EAST ) ; - return outerPanel; + public Wrapper(DialogFactory origFactory) { + this.origFactory = origFactory; } - public Dialog createDialog( String title, JPanel panel, boolean modal, + public Dialog createDialog( + String title, JPanel panel, boolean modal, JButton[] buttons, boolean sidebuttons, int defaultIndex, - int cancelIndex, ActionListener listener ) { - - // create the dialog with given content - JDialog d = new JDialog( (javax.swing.JFrame)null, title, modal ); - d.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); - d.getContentPane().add( panel, BorderLayout.CENTER); - - // Add the buttons to it - JPanel buttonPanel = createButtonPanel( buttons, sidebuttons ); - String buttonAlign = sidebuttons ? BorderLayout.EAST : BorderLayout.SOUTH; - d.getContentPane().add( buttonPanel, buttonAlign ); - - // add listener to buttons - if( listener != null ) { - for( int i = 0; i < buttons.length; i++ ) { - buttons[i].addActionListener( listener ); - } - } - - // register the default button, if available - if( defaultIndex >= 0 ) { - d.getRootPane().setDefaultButton( buttons[defaultIndex] ); - } - - // register the cancel button helpers, if available - if( cancelIndex >= 0 ) { - cancelButton = buttons[cancelIndex]; - // redirect the Esc key to Cancel button - d.getRootPane().registerKeyboardAction( - this, - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), - JComponent.WHEN_IN_FOCUSED_WINDOW - ); - - // listen on windowClosing and redirect it to Cancel button - d.addWindowListener( this ); - } - - d.pack(); - return d; - } - - public void actionPerformed(ActionEvent evt) { - cancelButton.doClick( 10 ); - } - - public void windowClosing( WindowEvent evt ) { - cancelButton.doClick( 10 ); - } + int cancelIndex, ActionListener listener) + { + return origFactory.createDialog(title, panel, modal, + buttons, sidebuttons, defaultIndex, cancelIndex, listener); } + } // End of Wraper class } File [changed]: DocumentFinder.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/DocumentFinder.java?r1=1.15&r2=1.16 Delta lines: +21 -1093 ----------------------- --- DocumentFinder.java 30 Jun 2006 19:17:48 -0000 1.15 +++ DocumentFinder.java 19 Jan 2007 05:21:30 -0000 1.16 @@ -34,214 +34,10 @@ /** * * @author Martin Roskanin + * @deprecated Without any replacement. */ public class DocumentFinder { - - private static FalseBlocksFinder falseBlocksFinder; - private static FalseFinder falseFinder; - private static WholeWordsBlocksFinder wholeWordsBlocksFinder; - private static RegExpBlocksFinder regExpBlocksFinder; - private static StringBlocksFinder stringBlocksFinder; - private static WholeWordsBwdFinder wholeWordsBwdFinder; - private static WholeWordsFwdFinder wholeWordsFwdFinder; - private static RegExpBwdFinder regExpBwdFinder; - private static RegExpFwdFinder regExpFwdFinder; - private static StringBwdFinder stringBwdFinder; - private static StringFwdFinder stringFwdFinder; - - /** Creates a new instance of DocumentFinder */ - private DocumentFinder() - { - } - - - private static DocFinder getFinder(BaseDocument doc, Map searchProps, boolean oppositeDir, boolean blocksFinder){ - String text = (String)searchProps.get(SettingsNames.FIND_WHAT); - if (text == null || text.length() == 0) { - if (blocksFinder) { - if (falseBlocksFinder == null){ - falseBlocksFinder = new FalseBlocksFinder(); - } - return falseBlocksFinder; - } else { - if (falseFinder == null){ - falseFinder = new FalseFinder(); - } - return falseFinder; - } - } - - Boolean b = (Boolean)searchProps.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean bwdSearch = (b != null && b.booleanValue()); - if (oppositeDir) { // negate for opposite direction search - bwdSearch = !bwdSearch; - } - - b = (Boolean)searchProps.get(SettingsNames.FIND_MATCH_CASE); - boolean matchCase = (b != null && b.booleanValue()); - b = (Boolean)searchProps.get(SettingsNames.FIND_SMART_CASE); - boolean smartCase = (b != null && b.booleanValue()); - b = (Boolean)searchProps.get(SettingsNames.FIND_WHOLE_WORDS); - boolean wholeWords = (b != null && b.booleanValue()); - - if (smartCase && !matchCase) { - int cnt = text.length(); - for (int i = 0; i < cnt; i++) { - if (Character.isUpperCase(text.charAt(i))) { - matchCase = true; - } - } - } - - b = (Boolean) searchProps.get(SettingsNames.FIND_REG_EXP); - boolean regExpSearch = (b!=null && b.booleanValue()); - - Pattern pattern = null; - if (regExpSearch){ - try{ - pattern = PatternCache.getPattern(text, matchCase); - if (pattern == null){ - pattern = (matchCase) ? Pattern.compile(text, Pattern.MULTILINE) : Pattern.compile(text, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // NOI18N - PatternCache.putPattern(text, matchCase, pattern); - } - }catch(PatternSyntaxException pse){ - if (!blocksFinder){ - NotifyDescriptor msg = new NotifyDescriptor.Message( - pse.getDescription(), NotifyDescriptor.ERROR_MESSAGE); - msg.setTitle(NbBundle.getBundle(BaseKit.class).getString("pattern-error-dialog-title")); //NOI18N - DialogDisplayer.getDefault().notify(msg); - } - PatternCache.putPattern(text, matchCase, null); - return null; - } - }else{ - PatternCache.clear(); - } - - if (blocksFinder) { - if (wholeWords && !regExpSearch) { - if (wholeWordsBlocksFinder == null){ - wholeWordsBlocksFinder = new WholeWordsBlocksFinder(); - } - wholeWordsBlocksFinder.setParams(doc, text, matchCase); - return wholeWordsBlocksFinder; - } else { - if (regExpSearch){ - if (regExpBlocksFinder == null){ - regExpBlocksFinder = new RegExpBlocksFinder(); - } - regExpBlocksFinder.setParams(pattern, matchCase); - return regExpBlocksFinder; - }else{ - if (stringBlocksFinder == null){ - stringBlocksFinder = new StringBlocksFinder(); - } - stringBlocksFinder.setParams(text, matchCase); - return stringBlocksFinder; - } - } - } else { - if (wholeWords && !regExpSearch) { - if (bwdSearch) { - if (wholeWordsBwdFinder == null){ - wholeWordsBwdFinder = new WholeWordsBwdFinder(); - } - wholeWordsBwdFinder.setParams(doc, text, matchCase); - return wholeWordsBwdFinder; - } else { - if (wholeWordsFwdFinder == null){ - wholeWordsFwdFinder = new WholeWordsFwdFinder(); - } - wholeWordsFwdFinder.setParams(doc, text, matchCase); - return wholeWordsFwdFinder; - } - } else { - if (regExpSearch){ - if (bwdSearch) { - if (regExpBwdFinder == null){ - regExpBwdFinder = new RegExpBwdFinder(); - } - regExpBwdFinder.setParams(pattern, matchCase); - return regExpBwdFinder; - } else { - if (regExpFwdFinder == null){ - regExpFwdFinder = new RegExpFwdFinder(); - } - regExpFwdFinder.setParams(pattern, matchCase); - return regExpFwdFinder; - } - }else{ - if (bwdSearch) { - if (stringBwdFinder == null){ - stringBwdFinder = new StringBwdFinder(); - } - stringBwdFinder.setParams(text, matchCase); - return stringBwdFinder; - } else { - if (stringFwdFinder == null){ - stringFwdFinder = new StringFwdFinder(); - } - stringFwdFinder.setParams(text, matchCase); - return stringFwdFinder; - } - } - } - } - } - - - private static FindReplaceResult findReplaceImpl(String replaceText, BaseDocument doc, int startOffset, int endOffset, Map props, - boolean oppositeDir) throws BadLocationException{ - int ret[] = new int[2]; - if (endOffset == -1){ - endOffset = doc.getLength(); - } - if (startOffset>endOffset){ - int temp = startOffset; - startOffset = endOffset; - endOffset = temp; - } - DocFinder finder = getFinder(doc, props, oppositeDir, false); - if (finder == null){ - return null; - } - finder.reset(); - CharSequence cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset); - if (cs==null) return null; - int findRet = finder.find(startOffset, cs); - if (!finder.isFound()){ - ret[0] = -1; - return new FindReplaceResult(ret, replaceText); - } - ret[0] = startOffset + findRet; - - if (finder instanceof StringFinder){ - int length = ((StringFinder)finder).getFoundLength(); - ret[1] = ret [0] + length; - } - - if (finder instanceof RegExpFinder){ - Matcher matcher = ((RegExpFinder)finder).getMatcher(); - if (matcher != null && replaceText != null){ - CharSequence foundString = cs.subSequence(ret[0]-startOffset, ret[1]-startOffset); - matcher.reset(foundString); - if (matcher.find()){ - try{ - replaceText = matcher.replaceFirst(replaceText); - }catch(IndexOutOfBoundsException ioobe){ - NotifyDescriptor msg = new NotifyDescriptor.Message( - ioobe.getLocalizedMessage(), NotifyDescriptor.ERROR_MESSAGE); - msg.setTitle(NbBundle.getBundle(BaseKit.class).getString("pattern-error-dialog-title")); //NOI18N - DialogDisplayer.getDefault().notify(msg); - return null; - } - } - } - } - return new FindReplaceResult(ret, replaceText); - } - /** * Finds in document * @@ -251,31 +47,18 @@ * that nothing was found. * @param props find properties */ - public static int[] find(BaseDocument doc, int startOffset, int endOffset, Map props, - boolean oppositeDir) throws BadLocationException{ - FindReplaceResult result = findReplaceImpl(null, doc, startOffset, endOffset, props, oppositeDir); - if (result == null){ - return null; - } - - return result.getFoundPositions(); + public static int[] find( + BaseDocument doc, int startOffset, int endOffset, Map props, + boolean oppositeDir + ) throws BadLocationException { + return org.netbeans.modules.editor.lib2.search.DocumentFinder.find(doc, startOffset, endOffset, props, oppositeDir); } - public static int[] findBlocks(BaseDocument doc, int startOffset, int endOffset, - Map props, int blocks[]) throws BadLocationException{ - BlocksFinder finder =(BlocksFinder) getFinder(doc, props, false, true); - if (finder == null){ - return blocks; - } - finder.reset(); - finder.setBlocks(blocks); - CharSequence cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset); - if (cs==null){ - return null; - } - finder.find(startOffset, cs); - int ret [] = finder.getBlocks(); - return ret; + public static int[] findBlocks( + BaseDocument doc, int startOffset, int endOffset, + Map props, int blocks[] + ) throws BadLocationException { + return org.netbeans.modules.editor.lib2.search.DocumentFinder.findBlocks(doc, startOffset, endOffset, props, blocks); } /** @@ -283,874 +66,19 @@ * regexp backreferences. * @return FindReplaceResult, that contains positions of found string and substituted replace expression */ - public static FindReplaceResult findReplaceResult(String replaceString, BaseDocument doc, int startOffset, int endOffset, Map props, - boolean oppositeDir) throws BadLocationException{ - return findReplaceImpl(replaceString, doc, startOffset, endOffset, props, oppositeDir); - } - - private interface DocFinder{ - - public int find(int initOffset, CharSequence data); - - public boolean isFound(); - - public void reset(); - } - - - private static final class FalseBlocksFinder extends AbstractBlocksFinder { - - public int find(int initOffset, CharSequence data) { - return -1; - } - - } - - /** Request non-existent position immediately */ - private static class FalseFinder extends AbstractFinder - implements StringFinder { - - public int find(int initOffset, CharSequence data) { - return -1; - } - - public int getFoundLength() { - return 0; - } - - } - - - - private static abstract class AbstractBlocksFinder extends AbstractFinder - implements BlocksFinder { - - private static int[] EMPTY_INT_ARRAY = new int[0]; - - private int[] blocks = EMPTY_INT_ARRAY; - - private int blocksInd; - - private boolean closed; - - public void reset() { - blocksInd = 0; - closed = false; - } - - public int[] getBlocks() { - if (!closed) { // not closed yet - closeBlocks(); - closed = true; - } - return blocks; - } - - public void setBlocks(int[] blocks) { - this.blocks = blocks; - closed = false; - } - - protected void addBlock(int blkStartPos, int blkEndPos) { - if (blocksInd == blocks.length) { - int[] dbl = new int[blocks.length * 2]; - System.arraycopy(blocks, 0, dbl, 0, blocks.length); - blocks = dbl; - } - blocks[blocksInd++] = blkStartPos; - blocks[blocksInd++] = blkEndPos; - } - - /** Insert closing sequence [-1, -1] */ - protected void closeBlocks() { - addBlock(-1, -1); - } - - public String debugBlocks() { - StringBuffer buf = new StringBuffer(); - int ind = 0; - while (blocks[ind] != -1) { - buf.append((ind/2 + 1) + ": [" + blocks[ind] + ", " + blocks[ind + 1] + "]\n"); // NOI18N - ind+= 2; - } - return buf.toString(); - } - - } - - /** Finder that constructs [begin-pos, end-pos] blocks. - * This is useful for highlight-search draw layer. - * The block-finders are always forward-search finders. - */ - private interface BlocksFinder extends DocFinder { - - /** Set the array into which the finder puts - * the position blocks. If the length of array is not sufficient - * the finder extends the array. The last block is set to [-1, -1]. - */ - public void setBlocks(int[] blocks); - - /** Get the array filled with position blocks. It is either - * original array passed to setBlocks() or the new array - * if the finder extended the array. - */ - public int[] getBlocks(); - - } - - - /** Abstract finder implementation. The only find() - * method must be redefined. - */ - private static abstract class AbstractFinder implements DocFinder { - - /** Was the string found? */ - protected boolean found; - - /** Was the string found? */ - public final boolean isFound() { - return found; - } - - /** Reset the finder */ - public void reset() { - found = false; - } - - } - - - /** Finder that looks for some search expression expressed by string. - * It can be either simple string - * or some form of regular expression expressed by string. - */ - private interface StringFinder extends DocFinder { - - /** Get the length of the found string. This is useful - * for regular expressions, because the length of the regular - * expression can be different than the length of the string - * that matched the expression. - */ - public int getFoundLength(); - - } - - - /** String forward finder that finds whole words only - * and that creates position blocks. - * There are some speed optimizations attempted. - */ - private static final class WholeWordsBlocksFinder extends AbstractBlocksFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - boolean insideWord; - - boolean firstCharWordPart; - - boolean wordFound; - - BaseDocument doc; - - public WholeWordsBlocksFinder() { - } - - public void setParams(BaseDocument doc, String s, boolean matchCase){ - this.matchCase = matchCase; - this.doc = doc; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - firstCharWordPart = doc.isIdentifierPart(chars[0]); - } - - public void reset() { - super.reset(); - insideWord = false; - wordFound = false; - stringInd = 0; - } - - public int find(int initOffset, CharSequence data) { - int offset = 0; - int limitPos = data.length(); - int limitOffset = limitPos - 1; - while (offset >= 0 && offset < limitPos) { - char ch = data.charAt(offset); - - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - - // whole word already found but must verify next char - if (wordFound) { - if (doc.isIdentifierPart(ch)) { // word continues - insideWord = firstCharWordPart; - offset -= chars.length - 1; - } else { - int blkEnd = initOffset + offset; - addBlock(blkEnd - chars.length, blkEnd); - insideWord = false; - offset++; - } - wordFound = false; - stringInd = 0; - continue; - } - - if (stringInd == 0) { // special case for first char - if (ch != chars[0] || insideWord) { // first char doesn't match - insideWord = doc.isIdentifierPart(ch); - offset++; - } else { // first char matches - stringInd = 1; // matched and not inside word - if (chars.length == 1) { - if (offset == limitOffset) { - int blkStart = initOffset + offset; - addBlock(blkStart, blkStart + 1); - } else { - wordFound = true; - } - } - offset++; - } - } else { // already matched at least one char - if (ch == chars[stringInd]) { // matches current char - stringInd++; - if (stringInd == chars.length) { // found whole string - if (offset == limitOffset) { - int blkEnd = initOffset + 1; - addBlock(blkEnd - stringInd, blkEnd); - } else { - wordFound = true; - } - } - offset++; - } else { // current char doesn't match, stringInd > 0 - offset += 1 - stringInd; - stringInd = 0; - insideWord = firstCharWordPart; - } - } - - } - return offset; - } - - } - - - /** String forward finder that creates position blocks */ - private static final class StringBlocksFinder - extends AbstractBlocksFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - public StringBlocksFinder() { - } - - public void setParams(String s, boolean matchCase){ - this.matchCase = matchCase; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - } - - public void reset() { - super.reset(); - stringInd = 0; - } - - public int find(int initOffset, CharSequence data) { - int offset = 0; - int endPos = data.length(); - while (offset >= 0 && offset < endPos) { - char ch = data.charAt(offset); - - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - if (ch == chars[stringInd]) { - - stringInd++; - if (stringInd == chars.length) { - int blkEnd = initOffset + offset + 1; - addBlock(blkEnd - stringInd, blkEnd); - stringInd = 0; - } - offset++; - } else { - offset += 1 - stringInd; - stringInd = 0; - } - - } - return offset; - } - - } - - - private static final class WholeWordsBwdFinder extends GenericBwdFinder - implements StringFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - boolean insideWord; - - boolean lastCharWordPart; - - boolean wordFound; - - int endInd; - - BaseDocument doc; - - public WholeWordsBwdFinder() { - } - - public void setParams(BaseDocument doc, String s, boolean matchCase){ - this.doc = doc; - this.matchCase = matchCase; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - endInd = chars.length - 1; - doc.isIdentifierPart(chars[endInd]); - } - - public int getFoundLength() { - return chars.length; - } - - public void reset() { - super.reset(); - insideWord = false; - wordFound = false; - stringInd = endInd; - } - - protected int scan(char ch, boolean lastChar) { - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - - // whole word already found but must verify next char - if (wordFound) { - if (doc.isIdentifierPart(ch)) { // word continues - wordFound = false; - insideWord = lastCharWordPart; - stringInd = endInd; - return endInd; - } else { - found = true; - return 1; - } - } - - if (stringInd == endInd) { // special case for last char - if (ch != chars[endInd] || insideWord) { // first char doesn't match - insideWord = doc.isIdentifierPart(ch); - return -1; - } else { // first char matches - stringInd = endInd - 1; // matched and not inside word - if (chars.length == 1) { - if (lastChar) { - found = true; - return 0; - } else { - wordFound = true; - return -1; - } - } - return -1; - } - } else { // already matched at least one char - if (ch == chars[stringInd]) { // matches current char - stringInd--; - if (stringInd == -1) { // found whole string - if (lastChar) { - found = true; - return 0; - } else { - wordFound = true; - return -1; - } - } - return -1; // successfully matched char, go to next char - } else { // current char doesn't match, stringInd > 0 - int back = chars.length - 2 - stringInd; - stringInd = endInd; - insideWord = lastCharWordPart; - return back; - } - } - } - } - - - /** Generic forward finder that simplifies the search process. */ - private static abstract class GenericFwdFinder extends AbstractFinder { - - public final int find(int initOffset, CharSequence chars) { - int offset = 0; - int limitPos = chars.length(); - int limitOffset = limitPos - 1; - while (offset >= 0 && offset < limitPos) { - offset += scan(chars.charAt(offset), (offset == limitOffset)); - if (found) { - break; - } - } - return offset; - } - - /** This function decides if it found a desired string or not. - * The function receives currently searched character and flag if it's - * the last one that is searched or not. - * @return if the function decides that - * it found a desired string it sets found = true and returns - * how many characters back the searched string begins in forward - * direction (0 stands for current character). - * For example if the function looks for word 'yes' and it gets - * 's' as parameter it sets found = true and returns -2. - * If the string is not yet found it returns how many characters it should go - * in forward direction (in this case it would usually be 1). - * The next searched character will be that one requested. - */ - protected abstract int scan(char ch, boolean lastChar); - - } - - /** Generic backward finder that simplifies the search process. */ - private static abstract class GenericBwdFinder extends AbstractFinder { - - public final int find(int initOffset, CharSequence chars) { - int offset = chars.length() - 1; - int offset2; - int limitPos = 0; - int limitOffset = chars.length(); - while (offset >= 0 && offset < limitOffset) { - offset += scan(chars.charAt(offset), (offset == limitOffset)); - if (found) { - break; - } - } - return offset; - } - - /** This function decides if it found a desired string or not. - * The function receives currently searched character and flag if it's - * the last one that is searched or not. - * @return if the function decides that - * it found a desired string it sets found = true and returns - * how many characters back the searched string begins in backward - * direction (0 stands for current character). It is usually 0 as the - * finder usually decides after the last required character but it's - * not always the case e.g. for whole-words-only search it can be 1 or so. - * If the string is not yet found it returns how many characters it should go - * in backward direction (in this case it would usually be -1). - * The next searched character will be that one requested. - */ - protected abstract int scan(char ch, boolean lastChar); - - } - - - private static final class WholeWordsFwdFinder extends GenericFwdFinder - implements StringFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - BaseDocument doc; - - boolean insideWord; - - boolean firstCharWordPart; - - boolean wordFound; - - public WholeWordsFwdFinder() { - } - - public void setParams(BaseDocument doc, String s, boolean matchCase){ - this.doc = doc; - this.matchCase = matchCase; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - firstCharWordPart = doc.isIdentifierPart(chars[0]); - } - - public int getFoundLength() { - return chars.length; - } - - public void reset() { - super.reset(); - insideWord = false; - wordFound = false; - stringInd = 0; - } - - protected int scan(char ch, boolean lastChar) { - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - - // whole word already found but must verify next char - if (wordFound) { - if (doc.isIdentifierPart(ch)) { // word continues - wordFound = false; - insideWord = firstCharWordPart; - stringInd = 0; - return 1 - chars.length; - } else { - found = true; - return -chars.length; - } - } - - if (stringInd == 0) { // special case for first char - if (ch != chars[0] || insideWord) { // first char doesn't match - insideWord = doc.isIdentifierPart(ch); - return 1; - } else { // first char matches - stringInd = 1; // matched and not inside word - if (chars.length == 1) { - if (lastChar) { - found = true; - return 0; - } else { - wordFound = true; - return 1; - } - } - return 1; - } - } else { // already matched at least one char - if (ch == chars[stringInd]) { // matches current char - stringInd++; - if (stringInd == chars.length) { // found whole string - if (lastChar) { - found = true; - return 1 - chars.length; // how many chars back the string starts - } else { - wordFound = true; - return 1; - } - } - return 1; // successfully matched char, go to next char - } else { // current char doesn't match, stringInd > 0 - int back = 1 - stringInd; - stringInd = 0; - insideWord = firstCharWordPart; - return back; // go back to search from the next to first char - } - } - } - - } - - private static class StringBwdFinder extends GenericBwdFinder - implements StringFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - int endInd; - - public StringBwdFinder() { - } - - public void setParams(String s, boolean matchCase){ - this.matchCase = matchCase; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - endInd = chars.length - 1; - } - - public int getFoundLength() { - return chars.length; - } - - public void reset() { - super.reset(); - stringInd = endInd; - } - - protected int scan(char ch, boolean lastChar) { - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - if (ch == chars[stringInd]) { - stringInd--; - if (stringInd == -1) { - found = true; - return 0; - } - return -1; - } else { - if (stringInd == endInd) { - return -1; - } else { - int back = chars.length - 2 - stringInd; - stringInd = endInd; - return back; - } - } - } - - } - - private static final class StringFwdFinder extends GenericFwdFinder - implements StringFinder { - - char chars[]; - - int stringInd; - - boolean matchCase; - - public StringFwdFinder() { - } - - public void setParams(String s, boolean matchCase){ - this.matchCase = matchCase; - chars = (matchCase ? s : s.toLowerCase()).toCharArray(); - } - - public int getFoundLength() { - return chars.length; - } - - public void reset() { - super.reset(); - stringInd = 0; - } - - protected int scan(char ch, boolean lastChar) { - if (!matchCase) { - ch = Character.toLowerCase(ch); - } - if (ch == chars[stringInd]) { - stringInd++; - if (stringInd == chars.length) { // found whole string - found = true; - return 1 - stringInd; // how many chars back the string starts - } - return 1; // successfully matched char, go to next char - } else { - if (stringInd == 0) { - return 1; - } else { - int back = 1 - stringInd; - stringInd = 0; - return back; - } - } - } + public static FindReplaceResult findReplaceResult( + String replaceString, BaseDocument doc, int startOffset, int endOffset, + Map props, boolean oppositeDir + ) throws BadLocationException { + org.netbeans.modules.editor.lib2.search.DocumentFinder.FindReplaceResult result = + org.netbeans.modules.editor.lib2.search.DocumentFinder.findReplaceResult( + replaceString, doc, startOffset, endOffset, props, oppositeDir); + return new FindReplaceResult(result.getFoundPositions(), result.getReplacedString()); } + private DocumentFinder() { - private abstract static class RegExpFinder extends AbstractFinder implements StringFinder{ - public abstract Matcher getMatcher(); - } - - // ----------------- regexp ---------------------- - private static class RegExpBwdFinder extends RegExpFinder{ - - boolean matchCase; - Pattern pattern; - int length = 0; - Matcher matcher; - - - public RegExpBwdFinder() { - } - - public Matcher getMatcher(){ - return matcher; - } - - public void setParams(Pattern pattern, boolean matchCase){ - this.matchCase = matchCase; - this.pattern = pattern; - } - - public int getFoundLength() { - return length; - } - - public void reset() { - super.reset(); - length = 0; - } - - private int lineFind (int lineStart, int lineEnd, CharSequence chars){ - matcher = pattern.matcher(chars.subSequence(lineStart, lineEnd)); - int ret = -1; - while (matcher.find()){ - int start = matcher.start(); - int end = matcher.end(); - length = end - start; - if (length <= 0){ - found = false; - return -1; - } - ret = start; - } - return ret; - } - - public int find(int initOffset, CharSequence chars) { - char ch; - - int charsEnd = chars.length() - 1; - int lineEnd = charsEnd; - int lineStart = charsEnd; - for (int i = charsEnd; i>=0; i--){ - ch = chars.charAt(i); - if (ch == '\n' || i==0){ - int retFind = lineFind (lineStart+((i==0)?0:1), lineEnd+1, chars); - if (retFind!=-1){ - found = true; - return i + retFind + ((i==0)?0:1); - } - lineStart--; - lineEnd = lineStart; - }else{ - lineStart--; - } - } - return -1; - } - - } - - private static final class RegExpFwdFinder extends RegExpFinder{ - - Pattern pattern; - boolean matchCase; - int length = 0; - Matcher matcher; - - public RegExpFwdFinder() { - } - - public Matcher getMatcher(){ - return matcher; - } - - public void setParams(Pattern pattern, boolean matchCase){ - this.matchCase = matchCase; - this.pattern = pattern; - } - - public int getFoundLength() { - return length; - } - - public void reset() { - super.reset(); - length = 0; - } - - public int find(int initOffset, CharSequence chars) { - matcher = pattern.matcher(chars); - if (matcher.find()){ - found = true; - int start = matcher.start(); - int end = matcher.end(); - length = end - start; - if (length <= 0){ - found = false; - return -1; - } - return start; - }else{ - return -1; - } - - } - } - - /** String forward finder that creates position blocks */ - private static final class RegExpBlocksFinder - extends AbstractBlocksFinder { - - Pattern pattern; - - int stringInd; - - boolean matchCase; - - public RegExpBlocksFinder() { - } - - public void setParams(Pattern pattern, boolean matchCase){ - this.pattern = pattern; - this.matchCase = matchCase; - } - - public void reset() { - super.reset(); - stringInd = 0; - } - - public int find(int initOffset, CharSequence data) { - Matcher matcher = pattern.matcher(data); - int ret = 0; - while (matcher.find()){ - int start = initOffset + matcher.start(); - int end = initOffset + matcher.end(); - addBlock(start, end); - ret = start; - } - return ret; - } - - } - - private static class PatternCache{ - - private static String cache_str; - private static boolean cache_matchCase; - private static Pattern cache_pattern; - - private PatternCache(){ - } - - public static void putPattern(String str, boolean matchCase, Pattern pattern){ - cache_str = str; - cache_matchCase = matchCase; - cache_pattern = pattern; - } - - public static Pattern getPattern(String str, boolean matchCase){ - if (str == null) return null; - if (str.equals(cache_str) && matchCase == cache_matchCase){ - return cache_pattern; - } - return null; - } - - public static void clear(){ - cache_str = null; - cache_matchCase = false; - cache_pattern = null; - } } public static class FindReplaceResult{ @@ -1169,7 +97,7 @@ public int[] getFoundPositions(){ return positions; } - } + } // End of FindReplaceResult class } File [changed]: DrawLayer.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/DrawLayer.java?r1=1.16&r2=1.17 Delta lines: +9 -1 ------------------- --- DrawLayer.java 30 Jun 2006 19:17:49 -0000 1.16 +++ DrawLayer.java 19 Jan 2007 05:21:31 -0000 1.17 @@ -31,6 +31,9 @@ * or by being activated through the draw-marks that it places * at the appropriate positions or it can mix these two approaches. * +* @deprecated Please use Highlighting SPI instead, for details see +* Editor Library 2. +* * @author Miloslav Metelka * @version 1.00 */ @@ -125,7 +128,12 @@ */ public int updateLineNumberContext(int lineNumber, DrawContext ctx); - /** Abstract implementation of the draw-layer. */ + /** + * Abstract implementation of the draw-layer. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static abstract class AbstractLayer implements DrawLayer { /** Name of this layer. The name of the layer must be unique among File [changed]: DrawLayerFactory.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/DrawLayerFactory.java?r1=1.47&r2=1.48 Delta lines: +86 -48 --------------------- --- DrawLayerFactory.java 30 Jun 2006 19:17:49 -0000 1.47 +++ DrawLayerFactory.java 19 Jan 2007 05:21:31 -0000 1.48 @@ -28,12 +28,14 @@ import javax.swing.text.View; /** -* Various draw layers are located here -* -* @author Miloslav Metelka -* @version 1.00 -*/ - + * Various draw layers are located here + * + * @author Miloslav Metelka + * @version 1.00 + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public class DrawLayerFactory { /** Syntax draw layer name */ @@ -196,6 +198,9 @@ * This layer assumes that both caretMark and selectionMark in * BaseCaret are properly served so that their active flags * are properly set. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. */ public static class CaretLayer extends DrawLayer.AbstractLayer { @@ -241,6 +246,9 @@ /** Highlight search layer highlights all occurences * of the searched string in text. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. */ public static class HighlightSearchLayer extends DrawLayer.AbstractLayer { @@ -326,6 +334,9 @@ /** Layer covering incremental search. There are just two positions * begining and end of the searched string + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. */ public static class IncSearchLayer extends DrawLayer.AbstractLayer implements SettingsChangeListener{ @@ -425,6 +436,10 @@ } + /** + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static class BlockSearchLayer extends DrawLayer.AbstractLayer implements SettingsChangeListener{ /** Coloring to use for highlighting */ @@ -519,40 +534,52 @@ } - /** Layer for guarded blocks */ - static class GuardedLayer extends ColorLineLayer { - - GuardedDocument doc; - - GuardedLayer() { - super(GUARDED_LAYER_NAME); - } - - public void init(DrawContext ctx) { - super.init(ctx); - doc = (GuardedDocument)ctx.getEditorUI().getDocument(); - } - - public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) { - boolean active; - if (mark != null) { - active = mark.activateLayer; - } else { - active = doc.isPosGuarded(ctx.getFragmentOffset()); - } - - return active; - } - - protected Coloring getColoring(DrawContext ctx) { - return ctx.getEditorUI().getColoring(SettingsNames.GUARDED_COLORING); - } - - } +// +// XXX: Deprecated and not used anymore. Can be removed. +// +// /** Layer for guarded blocks +// * +// * @deprecated Please use Highlighting SPI instead, for details see +// * Editor Library 2. +// */ +// static class GuardedLayer extends ColorLineLayer { +// +// GuardedDocument doc; +// +// GuardedLayer() { +// super(GUARDED_LAYER_NAME); +// } +// +// public void init(DrawContext ctx) { +// super.init(ctx); +// doc = (GuardedDocument)ctx.getEditorUI().getDocument(); +// } +// +// public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) { +// boolean active; +// if (mark != null) { +// active = mark.activateLayer; +// } else { +// active = doc.isPosGuarded(ctx.getFragmentOffset()); +// } +// +// return active; +// } +// +// protected Coloring getColoring(DrawContext ctx) { +// return ctx.getEditorUI().getColoring(SettingsNames.GUARDED_COLORING); +// } +// +// } - /** Style layer getting color settings from particular style */ + /** + * Style layer getting color settings from particular style + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static class StyleLayer extends DrawLayer.AbstractLayer { protected Style style; @@ -623,7 +650,12 @@ } - /** Test layer for coloring the specific words */ + /** + * Test layer for coloring the specific words + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static class WordColoringLayer extends DrawLayer.AbstractLayer { protected StringMap stringMap = new StringMap(); @@ -665,9 +697,15 @@ } - /** Annotation layer for drawing of annotations. Each mark which is stored in markChain has + // XXX: AnnotationLayer needs to be rewritten using the new Highlighting SPI. + /** + * Annotation layer for drawing of annotations. Each mark which is stored in markChain has * corresponding Annotation. More than one Annotation can share one mark. In this case - * the only one annotation is active and this must be drawn. */ + * the only one annotation is active and this must be drawn. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static class AnnotationLayer extends DrawLayer.AbstractLayer { /** Current coloring */ File [changed]: DrawLayerList.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/DrawLayerList.java?r1=1.9&r2=1.10 Delta lines: +17 -0 -------------------- --- DrawLayerList.java 30 Jun 2006 19:17:50 -0000 1.9 +++ DrawLayerList.java 19 Jan 2007 05:21:31 -0000 1.10 @@ -21,6 +21,11 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** Draw layer list stores multiple draw-layers sorted * according to their visibility which is the integer giving the z-order @@ -34,10 +39,14 @@ class DrawLayerList { + private static final Logger LOG = Logger.getLogger(DrawLayerList.class.getName()); + private static final DrawLayer[] EMPTY = new DrawLayer[0]; private DrawLayer[] layers = EMPTY; + private static final Set ISSUED_WARNINGS = Collections.synchronizedSet(new HashSet()); + private final ArrayList visibilityList = new ArrayList(); /** Add the new layer to the list depending on visibility. @@ -52,6 +61,14 @@ return false; } + String layerId = layer.getClass().getName() + layer.getName(); + if (!ISSUED_WARNINGS.contains(layerId)) { + ISSUED_WARNINGS.add(layerId); + LOG.log(Level.WARNING, "Using deprecated DrawLayer: " + layer.getName() + //NOI18N + " z-order: " + visibility + //NOI18N + " class: " + layer.getClass().getName() + " " + layer); //NOI18N + } + int indAdd = layers.length; for (int i = 0; i < layers.length; i++) { if (((Integer)visibilityList.get(i)).intValue() > visibility) { File [changed]: EditorUI.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/EditorUI.java?r1=1.87&r2=1.88 Delta lines: +26 -12 --------------------- --- EditorUI.java 29 Sep 2006 08:36:02 -0000 1.87 +++ EditorUI.java 19 Jan 2007 05:21:31 -0000 1.88 @@ -292,11 +292,6 @@ updateLineNumberWidth(0); drawLayerList.add(printDoc.getDrawLayerList()); - - // the fix of #37363 - drawLayerList.remove(DrawLayerFactory.GUARDED_LAYER_NAME); - drawLayerList.remove(DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_NAME); - drawLayerList.remove(DrawLayerFactory.INC_SEARCH_LAYER_NAME); } /** Gets the coloring map that can be shared by the components @@ -981,21 +976,40 @@ return drawLayerList; } - /** Find the layer with some layer name in the layer hierarchy */ + /** + * Find the layer with some layer name in the layer hierarchy. + * + *

Using of DrawLayers has been deprecated. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public DrawLayer findLayer(String layerName) { return drawLayerList.findLayer(layerName); } - /** Add new layer and use its priority to position it in the chain. + /** + * Add new layer and use its priority to position it in the chain. * If there's the layer with same visibility then the inserted layer * will be placed after it. * + *

Using of DrawLayers has been deprecated. + * * @param layer layer to insert into the chain + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. */ public boolean addLayer(DrawLayer layer, int visibility) { return drawLayerList.add(layer, visibility); } + /** + * Using of DrawLayers has been deprecated. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public DrawLayer removeLayer(String layerName) { return drawLayerList.remove(layerName); } File [changed]: FindSupport.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/FindSupport.java?r1=1.77&r2=1.78 Delta lines: +86 -691 ---------------------- --- FindSupport.java 30 Jun 2006 19:17:50 -0000 1.77 +++ FindSupport.java 19 Jan 2007 05:21:31 -0000 1.78 @@ -19,47 +19,27 @@ package org.netbeans.editor; -import java.awt.Frame; -import java.awt.Insets; -import java.awt.Rectangle; -import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashMap; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; import javax.swing.text.JTextComponent; import javax.swing.text.BadLocationException; -import javax.swing.text.Caret; -import javax.swing.text.Document; -import javax.swing.text.Position; -import org.netbeans.editor.DocumentFinder.FindReplaceResult; -import org.openide.util.NbBundle; +import org.netbeans.modules.editor.lib2.search.EditorFindSupport; /** * Find management * * @author Miloslav Metelka * @version 1.00 +* @deprecated Without any replacement. */ - public class FindSupport { - private static final String FOUND_LOCALE = "find-found"; // NOI18N - private static final String NOT_FOUND_LOCALE = "find-not-found"; // NOI18N - private static final String WRAP_START_LOCALE = "find-wrap-start"; // NOI18N - private static final String WRAP_END_LOCALE = "find-wrap-end"; // NOI18N - private static final String WRAP_BLOCK_START_LOCALE = "find-block-wrap-start"; // NOI18N - private static final String WRAP_BLOCK_END_LOCALE = "find-block-wrap-end"; // NOI18N - private static final String ITEMS_REPLACED_LOCALE = "find-items-replaced"; // NOI18N - public static final String REVERT_MAP = "revert-map"; // NOI18N - - private static final String SEARCH_BLOCK_START="search-block-start"; //NOI18N - private static final String SEARCH_BLOCK_END="search-block-end"; //NOI18N - - public static final String FIND_HISTORY_PROP = "find-history-prop"; //NOI18N - public static final String FIND_HISTORY_CHANGED_PROP = "find-history-changed-prop"; //NOI18N + public static final String REVERT_MAP = EditorFindSupport.REVERT_MAP; + public static final String FIND_HISTORY_PROP = EditorFindSupport.FIND_HISTORY_PROP; + public static final String FIND_HISTORY_CHANGED_PROP = EditorFindSupport.FIND_HISTORY_CHANGED_PROP; /** Shared instance of FindSupport class */ static FindSupport findSupport; @@ -87,74 +67,21 @@ } public Map getDefaultFindProperties() { - HashMap props = new HashMap(); - Class kitClass = BaseKit.class; - props.put(SettingsNames.FIND_WHAT, Settings.getValue( - kitClass, SettingsNames.FIND_WHAT)); - props.put(SettingsNames.FIND_REPLACE_WITH, Settings.getValue( - kitClass, SettingsNames.FIND_REPLACE_WITH)); - props.put(SettingsNames.FIND_HIGHLIGHT_SEARCH, Settings.getValue( - kitClass, SettingsNames.FIND_HIGHLIGHT_SEARCH)); - props.put(SettingsNames.FIND_INC_SEARCH, Settings.getValue( - kitClass, SettingsNames.FIND_INC_SEARCH)); - props.put(SettingsNames.FIND_BACKWARD_SEARCH, Settings.getValue( - kitClass, SettingsNames.FIND_BACKWARD_SEARCH)); - props.put(SettingsNames.FIND_WRAP_SEARCH, Settings.getValue( - kitClass, SettingsNames.FIND_WRAP_SEARCH)); - props.put(SettingsNames.FIND_MATCH_CASE, Settings.getValue( - kitClass, SettingsNames.FIND_MATCH_CASE)); - props.put(SettingsNames.FIND_SMART_CASE, Settings.getValue( - kitClass, SettingsNames.FIND_SMART_CASE)); - props.put(SettingsNames.FIND_WHOLE_WORDS, Settings.getValue( - kitClass, SettingsNames.FIND_WHOLE_WORDS)); - props.put(SettingsNames.FIND_REG_EXP, Settings.getValue( - kitClass, SettingsNames.FIND_REG_EXP)); - props.put(SettingsNames.FIND_HISTORY, Settings.getValue( - kitClass, SettingsNames.FIND_HISTORY)); - - return props; - } - - private int getBlockEndOffset(){ - Position pos = (Position) getFindProperties().get(SettingsNames.FIND_BLOCK_SEARCH_END); - return (pos != null) ? pos.getOffset() : -1; + return EditorFindSupport.getInstance().getDefaultFindProperties(); } public Map getFindProperties() { - if (findProps == null) { - findProps = getDefaultFindProperties(); - } - return findProps; + return EditorFindSupport.getInstance().getFindProperties(); } /** Get find property with specified name */ public Object getFindProperty(String name) { - return getFindProperties().get(name); - } - - private Map getValidFindProperties(Map props) { - return (props != null) ? props : getFindProperties(); + return EditorFindSupport.getInstance().getFindProperty(name); } int[] getBlocks(int[] blocks, BaseDocument doc, int startPos, int endPos) throws BadLocationException { - Map props = getValidFindProperties(null); - - Boolean b = (Boolean)props.get(SettingsNames.FIND_BLOCK_SEARCH); - boolean blockSearch = (b != null && b.booleanValue()); - Integer i = (Integer) props.get(SettingsNames.FIND_BLOCK_SEARCH_START); - int blockSearchStart = (i != null) ? i.intValue() : -1; - int blockSearchEnd = getBlockEndOffset(); - - if (blockSearch && blockSearchStart>-1 && blockSearchEnd >0){ - if (endPos>=blockSearchStart && startPos <=blockSearchEnd){ - startPos = Math.max(blockSearchStart, startPos); - endPos = Math.min(blockSearchEnd, endPos); - }else{ - return blocks; - } - } - return DocumentFinder.findBlocks(doc, startPos, endPos, props, blocks); + return EditorFindSupport.getInstance().getBlocks(blocks, doc, startPos, endPos); } /** Get find property without performing initialization @@ -162,28 +89,13 @@ * when it wants to query whether it should do highlight search. */ Object getPropertyNoInit(String name) { - if (findProps == null) { - return null; - } else { - return getFindProperty(name); - } + return EditorFindSupport.getInstance().getPropertyNoInit(name); } /** Set find property with specified name and fire change. */ public void putFindProperty(String name, Object newValue) { - Object oldValue = getFindProperty(name); - if ((oldValue == null && newValue == null) - || (oldValue != null && oldValue.equals(newValue)) - ) { - return; - } - if (newValue != null) { - getFindProperties().put(name, newValue); - } else { - getFindProperties().remove(name); - } - firePropertyChange(name, oldValue, newValue); + EditorFindSupport.getInstance().putFindProperty(name, newValue); } /** Add/replace properties from some other map @@ -192,324 +104,19 @@ * the property change is fired. */ public void putFindProperties(Map propsToAdd) { - if (!getFindProperties().equals(propsToAdd)) { - getFindProperties().putAll(propsToAdd); - firePropertyChange(null, null, null); - } + EditorFindSupport.getInstance().putFindProperties(propsToAdd); } public void setBlockSearchHighlight(int startSelection, int endSelection){ - JTextComponent c = Utilities.getLastActiveComponent(); - if (c==null) return; - EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); - DrawLayerFactory.BlockSearchLayer blockLayer - = (DrawLayerFactory.BlockSearchLayer)editorUI.findLayer( - DrawLayerFactory.BLOCK_SEARCH_LAYER_NAME); - Boolean b = (Boolean)getFindProperties().get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean back = (b != null && b.booleanValue()); - - if (startSelection >= endSelection){ - if (blockLayer != null) { - if (blockLayer.isEnabled()) { - blockLayer.setEnabled(false); - try { - editorUI.repaintBlock(blockLayer.getOffset(), blockLayer.getOffset()+blockLayer.getLength()); - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - } - } - }else{ - //init layer - if (blockLayer == null) { - blockLayer = new DrawLayerFactory.BlockSearchLayer(); - if (!editorUI.addLayer(blockLayer, - DrawLayerFactory.BLOCK_SEARCH_LAYER_VISIBILITY) - ) { - return; // couldn't add layer - } - } else { - if (blockLayer.isEnabled()) { - blockLayer.setEnabled(false); - try { - editorUI.repaintOffset(blockLayer.getOffset()); - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - } - } - - blockLayer.setEnabled(true); - blockLayer.setArea(startSelection, endSelection-startSelection); - try { - editorUI.repaintBlock(startSelection, endSelection); - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - return; - } - c.getCaret().setDot(back ? endSelection : startSelection); - } + EditorFindSupport.getInstance().setBlockSearchHighlight(startSelection, endSelection); } public boolean incSearch(Map props, int caretPos) { - props = getValidFindProperties(props); - - // if regexp terminate incSearch - Boolean b = (Boolean)props.get(SettingsNames.FIND_REG_EXP); - if (b !=null && b.booleanValue()){ - return false; - } - - b = (Boolean)props.get(SettingsNames.FIND_INC_SEARCH); - if (b != null && b.booleanValue()) { // inc search enabled - JTextComponent c = Utilities.getLastActiveComponent(); - if (c != null && c.getDocument() instanceof BaseDocument) { - BaseDocument doc = (BaseDocument)c.getDocument(); - b = (Boolean)props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean back = (b != null && b.booleanValue()); - b = (Boolean)props.get(SettingsNames.FIND_BLOCK_SEARCH); - boolean blockSearch = (b != null && b.booleanValue()); - Integer i = (Integer) props.get(SettingsNames.FIND_BLOCK_SEARCH_START); - int blockSearchStart = (i != null) ? i.intValue() : -1; - - Position endPos = (Position) props.get(SettingsNames.FIND_BLOCK_SEARCH_END); - int blockSearchEnd = (endPos != null) ? endPos.getOffset() : -1; - int endOffset = (back) ? 0 : -1; - int pos; - try { - int start = (blockSearch && blockSearchStart > -1) ? blockSearchStart : 0; - int end = (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : -1; - if (start>0 && end == -1) return false; - int findRet[] = findInBlock(c, caretPos, - start, - end, - props, false); - - if (findRet == null) { - incSearchReset(); - return false; - } - pos = findRet[0]; - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - return false; - } - - // possibly create incSearch layer - BaseTextUI ui = (BaseTextUI)c.getUI(); - EditorUI editorUI = ui.getEditorUI(); - DrawLayerFactory.IncSearchLayer incLayer - = (DrawLayerFactory.IncSearchLayer)editorUI.findLayer( - DrawLayerFactory.INC_SEARCH_LAYER_NAME); - if (incLayer == null) { - incLayer = new DrawLayerFactory.IncSearchLayer(); - if (!editorUI.addLayer(incLayer, - DrawLayerFactory.INC_SEARCH_LAYER_VISIBILITY) - ) { - return false; // couldn't add layer - } - } else { - if (incLayer.isEnabled()) { - incLayer.setEnabled(false); - try { - editorUI.repaintOffset(incLayer.getOffset()); - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - } - } - - if (pos >= 0) { - String s = (String)props.get(SettingsNames.FIND_WHAT); - int len = (s != null) ? s.length() : 0; - if (len > 0) { - if (c.getSelectionEnd() > c.getSelectionStart()){ - c.select(caretPos, caretPos); - } - incLayer.setInversion(!blockSearch); - incLayer.setEnabled(true); - incLayer.setArea(pos, len); - // reset higlighting - Map defaultProps = getValidFindProperties(null); - String findWhatDef = (String)defaultProps.get(SettingsNames.FIND_WHAT); - if (findWhatDef!=null && findWhatDef.length()>0){ - defaultProps.put(SettingsNames.FIND_WHAT, ""); //NOI18N - editorUI.getComponent().repaint(); - } - try { - editorUI.repaintOffset(pos); - ensureVisible(c, pos, pos); - - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - return true; - } - } else { // string not found - // !!! ((BaseCaret)c.getCaret()).dispatchUpdate(); - } - - } - } else { // inc search not enabled - incSearchReset(); - } - return false; + return EditorFindSupport.getInstance().incSearch(props, caretPos); } public void incSearchReset() { - JTextComponent c = Utilities.getLastActiveComponent(); - if (c==null) return; // #19558 bugfix. Editor window has been closed => c==null - EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); - DrawLayerFactory.IncSearchLayer incLayer - = (DrawLayerFactory.IncSearchLayer)editorUI.findLayer( - DrawLayerFactory.INC_SEARCH_LAYER_NAME); - if (incLayer != null) { - if (incLayer.isEnabled()) { - incLayer.setEnabled(false); - try { - editorUI.repaintOffset(incLayer.getOffset()); - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - } - } - } - - - private boolean isBackSearch(Map props, boolean oppositeDir) { - Boolean b = (Boolean)props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean back = (b != null && b.booleanValue()); - if (oppositeDir) { - back = !back; - } - return back; - } - - private void selectText(JTextComponent c, int start, int end, boolean back){ - Caret caret = c.getCaret(); - ensureVisible(c, start, end); - if (back) { - caret.setDot(end); - caret.moveDot(start); - } else { // forward direction - caret.setDot(start); - caret.moveDot(end); - } - } - - private void ensureVisible(JTextComponent c, int startOffset, int endOffset) { - Class kitClass = Utilities.getKitClass(c); - if (kitClass == null) { - kitClass = BaseKit.class; - } - ensureVisible(c, startOffset, endOffset, - (Insets)Settings.getValue(kitClass, SettingsNames.SCROLL_FIND_INSETS)); - } - /** - * Ensure that the given region will be visible in the view - * with the appropriate find insets. - */ - private void ensureVisible(JTextComponent c, int startOffset, int endOffset, Insets extraInsets) { - try { - Rectangle startBounds = c.modelToView(startOffset); - Rectangle endBounds = c.modelToView(endOffset); - if (startBounds != null && endBounds != null) { - startBounds.add(endBounds); - if (extraInsets != null) { - Rectangle visibleBounds = c.getVisibleRect(); - int extraTop = (extraInsets.top < 0) - ? -extraInsets.top * visibleBounds.height / 100 // percentage - : extraInsets.top * endBounds.height; // line count - startBounds.y -= extraTop; - startBounds.height += extraTop; - startBounds.height += (extraInsets.bottom < 0) - ? -extraInsets.bottom * visibleBounds.height / 100 // percentage - : extraInsets.bottom * endBounds.height; // line count - int extraLeft = (extraInsets.left < 0) - ? -extraInsets.left * visibleBounds.width / 100 // percentage - : extraInsets.left * endBounds.width; // char count - startBounds.x -= extraLeft; - startBounds.width += extraLeft; - startBounds.width += (extraInsets.right < 0) - ? -extraInsets.right * visibleBounds.width / 100 // percentage - : extraInsets.right * endBounds.width; // char count - } - c.scrollRectToVisible(startBounds); - } - } catch (BadLocationException e) { - // do not scroll - } - } - - private FindReplaceResult findReplaceImpl(String replaceExp, Map props, boolean oppositeDir){ - incSearchReset(); - props = getValidFindProperties(props); - boolean back = isBackSearch(props, oppositeDir); - JTextComponent c = Utilities.getLastActiveComponent(); - Object findWhat = props.get(SettingsNames.FIND_WHAT); - if (findWhat == null) { // nothing to search for - return null; - } - - String exp = "'" + findWhat + "' "; // NOI18N - if (c != null) { - Utilities.clearStatusText(c); - Caret caret = c.getCaret(); - int dotPos = caret.getDot(); - if (findWhat.equals(c.getSelectedText())) { - Object dp = props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean direction = (dp != null) ? ((Boolean)dp).booleanValue() : false; - - if (dotPos == (oppositeDir ^ direction ? c.getSelectionEnd() : c.getSelectionStart())) - dotPos += (oppositeDir ^ direction ? -1 : 1); - } - - Boolean b = (Boolean)props.get(SettingsNames.FIND_BLOCK_SEARCH); - boolean blockSearch = (b != null && b.booleanValue()); - Integer i = (Integer) props.get(SettingsNames.FIND_BLOCK_SEARCH_START); - int blockSearchStart = (i != null) ? i.intValue() : -1; - int blockSearchEnd = getBlockEndOffset(); - - try { - FindReplaceResult result = findReplaceInBlock(replaceExp, c, dotPos, - (blockSearch && blockSearchStart > -1) ? blockSearchStart : 0, - (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : -1, - props, oppositeDir); - int[] blk = null; - if (result != null){ - blk = result.getFoundPositions(); - } - if (blk != null) { - selectText(c, blk[0], blk[1], back); - JumpList.checkAddEntry(); - String msg = exp + NbBundle.getBundle(BaseKit.class).getString(FOUND_LOCALE) - + ' ' + Utilities.debugPosition((BaseDocument)c.getDocument(), blk[0]); - if (blk[2] == 1) { // wrap was done - msg += "; "; // NOI18N - if (blockSearch && blockSearchEnd>0 && blockSearchStart >-1){ - msg += back ? NbBundle.getBundle(BaseKit.class).getString(WRAP_BLOCK_END_LOCALE) - : NbBundle.getBundle(BaseKit.class).getString(WRAP_BLOCK_START_LOCALE); - }else{ - msg += back ? NbBundle.getBundle(BaseKit.class).getString(WRAP_END_LOCALE) - : NbBundle.getBundle(BaseKit.class).getString(WRAP_START_LOCALE); - } - Utilities.setStatusBoldText(c, msg); - c.getToolkit().beep(); - } else { - Utilities.setStatusText(c, msg); - } - return result; - } else { // not found - Utilities.setStatusBoldText(c, exp + NbBundle.getBundle(BaseKit.class).getString( - NOT_FOUND_LOCALE)); - // issue 14189 - selection was not removed - c.getCaret().setDot(c.getCaret().getDot()); - } - } catch (BadLocationException e) { - Utilities.annotateLoggable(e); - } - } - return null; + EditorFindSupport.getInstance().incSearchReset(); } /** Find the text from the caret position. @@ -517,77 +124,7 @@ * @param oppositeDir whether search in opposite direction */ public boolean find(Map props, boolean oppositeDir) { - FindReplaceResult result = findReplaceImpl(null, props, oppositeDir); - return (result != null); - } - - private FindReplaceResult findReplaceInBlock(String replaceExp, JTextComponent c, int startPos, int blockStartPos, - int blockEndPos, Map props, boolean oppositeDir) throws BadLocationException { - if (c != null) { - props = getValidFindProperties(props); - BaseDocument doc = (BaseDocument)c.getDocument(); - int pos = -1; - boolean wrapDone = false; - String replaced = null; - - boolean back = isBackSearch(props, oppositeDir); - Boolean b = (Boolean)props.get(SettingsNames.FIND_WRAP_SEARCH); - boolean wrap = (b != null && b.booleanValue()); - int docLen = doc.getLength(); - if (blockEndPos == -1) { - blockEndPos = docLen; - } - - int retFind[]; - while (true) { - //pos = doc.find(sf, startPos, back ? blockStartPos : blockEndPos); - int off1 = startPos; - int off2 = back ? blockStartPos : blockEndPos; - FindReplaceResult result = DocumentFinder.findReplaceResult(replaceExp, doc, Math.min(off1, off2), Math.max(off1, off2), - props, oppositeDir ); - if (result == null){ - return null; - } - retFind = result.getFoundPositions(); - replaced = result.getReplacedString(); - if (retFind == null){ - break; - } - pos = retFind[0]; - - if (pos != -1) { - break; - } - - if (wrap) { - if (back) { - //Bug #20552 the wrap search check whole document - //instead of just the remaining not-searched part to be - //able to find expressions with the cursor in it - - //blockStartPos = startPos; - startPos = blockEndPos; - } else { - //blockEndPos = startPos; - startPos = blockStartPos; - } - wrapDone = true; - wrap = false; // only one loop - } else { // no wrap set - break; - } - - } - - if (pos != -1) { - int[] ret = new int[3]; - ret[0] = pos; - ret[1] = retFind[1]; - ret[2] = wrapDone ? 1 : 0; - return new FindReplaceResult(ret, replaced); - } - } - return null; + return EditorFindSupport.getInstance().find(props, oppositeDir); } /** Find the searched expression @@ -606,249 +143,80 @@ * ret[2] - 1 or 0 when wrap was or wasn't performed in order to find the string */ public int[] findInBlock(JTextComponent c, int startPos, int blockStartPos, - int blockEndPos, Map props, boolean oppositeDir) throws BadLocationException { - FindReplaceResult result = findReplaceInBlock(null, c, startPos, blockStartPos, - blockEndPos, props, oppositeDir); - return result == null ? null : result.getFoundPositions(); - } - - public boolean replace(Map props, boolean oppositeDir) - throws BadLocationException { - incSearchReset(); - props = getValidFindProperties(props); - Boolean b = (Boolean)props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean back = (b != null && b.booleanValue()); - if (oppositeDir) { - back = !back; - } - - b = (Boolean)props.get(SettingsNames.FIND_BLOCK_SEARCH); - boolean blockSearch = (b != null && b.booleanValue()); - Integer i = (Integer) props.get(SettingsNames.FIND_BLOCK_SEARCH_START); - int blockSearchStart = (i != null) ? i.intValue() : -1; - int blockSearchEnd = getBlockEndOffset(); - - JTextComponent c = Utilities.getLastActiveComponent(); - if (c != null) { - String s = (String)props.get(SettingsNames.FIND_REPLACE_WITH); - Caret caret = c.getCaret(); - if (caret.isSelectionVisible()){ - int dotPos = caret.getDot(); - Object dp = props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean direction = (dp != null) ? ((Boolean)dp).booleanValue() : false; - dotPos = (oppositeDir ^ direction ? c.getSelectionEnd() : c.getSelectionStart()); - c.setCaretPosition(dotPos); - } - - FindReplaceResult result = findReplaceImpl(s, props, oppositeDir); - if (result!=null){ - s = result.getReplacedString(); - } else { - return false; - } - - BaseDocument doc = (BaseDocument)c.getDocument(); - int startPos = c.getSelectionStart(); - int len = c.getSelectionEnd() - startPos; - doc.atomicLock(); - try { - if (len > 0) { - doc.remove(startPos, len); + int blockEndPos, Map props, boolean oppositeDir) throws BadLocationException + { + return EditorFindSupport.getInstance().findInBlock( + c, startPos, blockStartPos, blockEndPos, props, oppositeDir); } - if (s != null && s.length() > 0) { - doc.insertString(startPos, s, null); - } - } finally { - doc.atomicUnlock(); - if (blockSearch){ - setBlockSearchHighlight(blockSearchStart, getBlockEndOffset()); - } - } - - // adjust caret pos after replace operation - int adjustedCaretPos = (back || s == null) ? startPos : startPos + s.length(); - caret.setDot(adjustedCaretPos); - } - - return true; + public boolean replace(Map props, boolean oppositeDir) throws BadLocationException { + return EditorFindSupport.getInstance().replace(props, oppositeDir); } public void replaceAll(Map props) { - incSearchReset(); - JTextComponent c = Utilities.getLastActiveComponent(); - BaseDocument doc = (BaseDocument)c.getDocument(); - int maxCnt = doc.getLength(); - int replacedCnt = 0; - int totalCnt = 0; - - props = getValidFindProperties(props); - props = new HashMap(props); - String replaceWithOriginal = (String)props.get(SettingsNames.FIND_REPLACE_WITH); - - Boolean b = (Boolean)props.get(SettingsNames.FIND_BLOCK_SEARCH); - boolean blockSearch = (b != null && b.booleanValue()); - b = (Boolean)props.get(SettingsNames.FIND_WRAP_SEARCH); - boolean wrapSearch = (b != null && b.booleanValue()); - b = (Boolean)props.get(SettingsNames.FIND_BACKWARD_SEARCH); - boolean backSearch = (b != null && b.booleanValue()); - - if (wrapSearch){ - props.put(SettingsNames.FIND_WRAP_SEARCH, Boolean.FALSE); - props.put(SettingsNames.FIND_BACKWARD_SEARCH, Boolean.FALSE); - firePropertyChange(null, null, null); - } - - Integer i = (Integer) props.get(SettingsNames.FIND_BLOCK_SEARCH_START); - int blockSearchStart = (i != null) ? i.intValue() : -1; - int blockSearchEnd = getBlockEndOffset(); - - if (c != null) { - doc.atomicLock(); - try { - int startPosWholeSearch = 0; - int endPosWholeSearch = -1; - int caretPos = c.getCaret().getDot(); - - if (!wrapSearch){ - if (backSearch){ - startPosWholeSearch = 0; - endPosWholeSearch = caretPos; - }else{ - startPosWholeSearch = caretPos; - endPosWholeSearch = -1; - } - } - - int actualPos = wrapSearch ? 0 : c.getCaret().getDot(); - - int pos = (blockSearch && blockSearchStart > -1) ? ( backSearch ? blockSearchEnd : blockSearchStart) : actualPos; // actual position - - while (true) { - blockSearchEnd = getBlockEndOffset(); - FindReplaceResult result = findReplaceInBlock(replaceWithOriginal, c, pos, - (blockSearch && blockSearchStart > -1) ? blockSearchStart : startPosWholeSearch, - (blockSearch && blockSearchEnd > 0) ? blockSearchEnd : endPosWholeSearch, - props, false); - if (result == null){ - break; - } - int[] blk = result.getFoundPositions(); - String replaceWith = result.getReplacedString(); - if (blk == null) { - break; - } - totalCnt++; - int len = blk[1] - blk[0]; - boolean skip = false; // cannot remove (because of guarded block)? - try { - doc.remove(blk[0], len); - } catch (GuardedException e) { - // replace in guarded block - skip = true; - } - if (skip) { - pos = blk[0] + len; - - } else { // can and will insert the new string - if (replaceWith != null && replaceWith.length() > 0) { - doc.insertString(blk[0], replaceWith, null); - } - pos = blk[0] + ((replaceWith != null) ? replaceWith.length() : 0); - replacedCnt++; - } - } - - // Display message about replacement - if (totalCnt == 0){ - Object findWhat = props.get(SettingsNames.FIND_WHAT); - String exp = "'' "; //NOI18N - if (findWhat != null) { // nothing to search for - exp = "'" + findWhat + "' "; // NOI18N - } - Utilities.setStatusBoldText(c, exp + NbBundle.getBundle(BaseKit.class).getString( - NOT_FOUND_LOCALE)); - }else{ - MessageFormat fmt = new MessageFormat( - NbBundle.getBundle(BaseKit.class).getString(ITEMS_REPLACED_LOCALE)); - String msg = fmt.format(new Object[] { new Integer(replacedCnt), new Integer(totalCnt) }); - Utilities.setStatusText(c, msg); - } - - } catch (BadLocationException e) { - e.printStackTrace(); - } finally { - doc.atomicUnlock(); - if (blockSearch){ - setBlockSearchHighlight(blockSearchStart, getBlockEndOffset()); - } - } - } + EditorFindSupport.getInstance().replaceAll(props); } - /** Get position of wrap mark for some document */ - public int getWrapSearchMarkPos(BaseDocument doc) { - Mark mark = (Mark)doc.getProperty(BaseDocument.WRAP_SEARCH_MARK_PROP); - try { - return (mark != null) ? mark.getOffset() : doc.getLength(); - } catch (InvalidMarkException e) { - throw new RuntimeException(); // shouldn't happen - } - } - - /** Set new position of wrap mark for some document */ - public void setWrapSearchMarkPos(BaseDocument doc, int pos) { - //!!! - } +// TODO: remove +// /** Get position of wrap mark for some document */ +// public int getWrapSearchMarkPos(BaseDocument doc) { +// return EditorFindSupport.getInstance().getWrapSearchMarkPos(doc); +// } +// +// /** Set new position of wrap mark for some document */ +// public void setWrapSearchMarkPos(BaseDocument doc, int pos) { +// EditorFindSupport.getInstance().setWrapSearchMarkPos(doc, pos); +// } /** Add weak listener to listen to change of any property. The caller must * hold the listener object in some instance variable to prevent it * from being garbage collected. */ public void addPropertyChangeListener(PropertyChangeListener l) { - changeSupport.addPropertyChangeListener(l); + EditorFindSupport.getInstance().addPropertyChangeListener(new WeakPropL(l)); } - public synchronized void addPropertyChangeListener(String findPropertyName, - PropertyChangeListener l) { - changeSupport.addPropertyChangeListener(findPropertyName, l); + public synchronized void addPropertyChangeListener( + String findPropertyName, PropertyChangeListener l) + { + EditorFindSupport.getInstance().addPropertyChangeListener( + findPropertyName, new WeakPropL(l)); } /** Remove listener for changes in properties */ public void removePropertyChangeListener(PropertyChangeListener l) { - changeSupport.removePropertyChangeListener(l); + // no-op } void firePropertyChange(String settingName, Object oldValue, Object newValue) { - changeSupport.firePropertyChange(this, settingName, oldValue, newValue); + EditorFindSupport.getInstance().firePropertyChange(settingName, oldValue, newValue); } public void setHistory(List/**/ spwList){ - this.historyList = new ArrayList(spwList); - firePropertyChange(FIND_HISTORY_CHANGED_PROP,null,null); + EditorFindSupport.getInstance().setHistory(spwList); } public List/**/ getHistory(){ - return historyList; + return EditorFindSupport.getInstance().getHistory(); } - public void setLastSelected(SearchPatternWrapper spw){ - this.lastSelected = spw; - Map props = getFindProperties(); - if (spw == null) return; - props.put(SettingsNames.FIND_WHAT, spw.getSearchExpression()); - props.put(SettingsNames.FIND_MATCH_CASE, Boolean.valueOf(spw.isMatchCase())); - props.put(SettingsNames.FIND_REG_EXP, Boolean.valueOf(spw.isRegExp())); - props.put(SettingsNames.FIND_WHOLE_WORDS, Boolean.valueOf(spw.isWholeWords())); + public void setLastSelected(SearchPatternWrapper spw) { + EditorFindSupport.SPW editorSpw = new EditorFindSupport.SPW( + spw.getSearchExpression(), spw.isWholeWords(), spw.isMatchCase(), spw.isRegExp() + ); + EditorFindSupport.getInstance().setLastSelected(editorSpw); } public SearchPatternWrapper getLastSelected(){ - return lastSelected; + EditorFindSupport.SPW spw = EditorFindSupport.getInstance().getLastSelected(); + return new SearchPatternWrapper(spw.getSearchExpression(), spw.isWholeWords(), spw.isMatchCase(), spw.isRegExp()); } public void addToHistory(SearchPatternWrapper spw){ - if (spw == null) return; - firePropertyChange(FIND_HISTORY_PROP, null, spw); + EditorFindSupport.SPW editorSpw = new EditorFindSupport.SPW( + spw.getSearchExpression(), spw.isWholeWords(), spw.isMatchCase(), spw.isRegExp() + ); + EditorFindSupport.getInstance().addToHistory(editorSpw); } public static class SearchPatternWrapper{ @@ -918,5 +286,32 @@ sb.append(regExp); return sb.toString(); } + } // End of SearchPatternWrapper class + + private static final class WeakPropL extends WeakReference implements PropertyChangeListener { + + public WeakPropL(PropertyChangeListener origL) { + super(origL); + } + + public void propertyChange(PropertyChangeEvent evt) { + PropertyChangeListener origL = (PropertyChangeListener) get(); + if (origL != null) { + origL.propertyChange(new PropertyChangeEvent(this, evt.getPropertyName(), + convert(evt.getOldValue()), convert(evt.getNewValue()))); + } else { + EditorFindSupport.getInstance().removePropertyChangeListener(this); + } + } + + private Object convert(Object o) { + if (o instanceof EditorFindSupport.SPW) { + EditorFindSupport.SPW spw = (EditorFindSupport.SPW) o; + return new SearchPatternWrapper( + spw.getSearchExpression(), spw.isWholeWords(), spw.isMatchCase(), spw.isRegExp()); + } else { + return o; + } } + } // End of WeakPropL class } File [changed]: GuardedDocument.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/GuardedDocument.java?r1=1.20&r2=1.21 Delta lines: +18 -8 -------------------- --- GuardedDocument.java 30 Jun 2006 19:17:51 -0000 1.20 +++ GuardedDocument.java 19 Jan 2007 05:21:31 -0000 1.21 @@ -19,25 +19,20 @@ package org.netbeans.editor; -import java.beans.PropertyChangeEvent; -import java.lang.ref.WeakReference; import java.text.MessageFormat; import java.util.Hashtable; import java.util.Enumeration; import java.awt.Color; import java.awt.Font; import javax.swing.text.BadLocationException; -import javax.swing.text.Document; import javax.swing.text.StyledDocument; import javax.swing.text.Style; import javax.swing.text.Element; import javax.swing.text.AttributeSet; import javax.swing.text.StyleContext; -import javax.swing.event.DocumentListener; import javax.swing.event.DocumentEvent; +import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; -import javax.swing.undo.CannotUndoException; -import javax.swing.undo.CannotRedoException; import org.openide.util.NbBundle; /** * Extension to the guarded document that implements @@ -105,8 +100,17 @@ super(kitClass, addToRegistry); this.styles = styles; stylesToLayers = new Hashtable(5); - addLayer(new DrawLayerFactory.GuardedLayer(), DrawLayerFactory.GUARDED_LAYER_VISIBILITY); - guardedBlockChain = new MarkBlockChain.LayerChain(this, DrawLayerFactory.GUARDED_LAYER_NAME); + guardedBlockChain = new MarkBlockChain(this) { + protected Mark createBlockStartMark() { + MarkFactory.ContextMark startMark = new MarkFactory.ContextMark(Position.Bias.Forward, false); + return startMark; + } + + protected Mark createBlockEndMark() { + MarkFactory.ContextMark endMark = new MarkFactory.ContextMark(Position.Bias.Backward, false); + return endMark; + } + }; } /** Get the chain of the guarded blocks */ @@ -433,6 +437,12 @@ return new Font("Default",Font.BOLD,12); // NOI18N } + /** + * Using of DrawLayers has been deprecated. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ protected DrawLayer addStyledLayer(String layerName, Style style) { if (layerName != null) { try { File [changed]: ImplementationProvider.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ImplementationProvider.java?r1=1.8&r2=1.9 Delta lines: +45 -6 -------------------- --- ImplementationProvider.java 30 Jun 2006 19:17:52 -0000 1.8 +++ ImplementationProvider.java 19 Jan 2007 05:21:31 -0000 1.9 @@ -22,6 +22,8 @@ import java.util.ResourceBundle; import javax.swing.Action; import javax.swing.text.JTextComponent; +import org.netbeans.modules.editor.lib2.EditorImplementation; +import org.netbeans.spi.editor.EditorImplementationProvider; /** This is provider of implementation. This package (org.netbeans.editor) * represent editor core which can be used independently on the rest of NetBeans. @@ -34,30 +36,31 @@ * * @author David Konecny * @since 10/2001 + * @deprecated See org.netbeans.spi.editor.lib2.EditorImplementationProvider */ -abstract public class ImplementationProvider { +public abstract class ImplementationProvider { - private static ImplementationProvider provider = null; + private static final ImplementationProvider PROVIDER = new ProviderBridge(); /** Returns currently registered provider */ public static ImplementationProvider getDefault() { - return provider; + return PROVIDER; } /** Register your own provider through this method */ public static void registerDefault(ImplementationProvider prov) { - provider = prov; + EditorImplementation.getDefault().setExternalProvider(new Wrapper(prov)); } /** Returns ResourceBundle for the given class.*/ - abstract public ResourceBundle getResourceBundle(String localizer); + public abstract ResourceBundle getResourceBundle(String localizer); /** This is temporary method which allows core editor to access * glyph gutter action. These actions are then used when user clicks * on glyph gutter. In next version this should be removed and redesigned * as suggested in issue #16762 */ - abstract public Action[] getGlyphGutterActions(JTextComponent target); + public abstract Action[] getGlyphGutterActions(JTextComponent target); /** Activates the given component or one of its ancestors. * @return whether the component or one of its ancestors was succesfuly activated @@ -66,4 +69,40 @@ return false; } + private static final class ProviderBridge extends ImplementationProvider { + + public ResourceBundle getResourceBundle(String localizer) { + return EditorImplementation.getDefault().getResourceBundle(localizer); + } + + public Action[] getGlyphGutterActions(JTextComponent target) { + return EditorImplementation.getDefault().getGlyphGutterActions(target); + } + + public boolean activateComponent(JTextComponent c) { + return EditorImplementation.getDefault().activateComponent(c); + } + } // End of ProviderBridge class + + private static final class Wrapper implements EditorImplementationProvider { + + private ImplementationProvider origProvider; + + public Wrapper(ImplementationProvider origProvider) { + this.origProvider = origProvider; + } + + public ResourceBundle getResourceBundle(String localizer) { + return origProvider.getResourceBundle(localizer); + } + + public Action[] getGlyphGutterActions(JTextComponent target) { + return origProvider.getGlyphGutterActions(target); + } + + public boolean activateComponent(JTextComponent c) { + return origProvider.activateComponent(c); + } + + } // End of Wrapper class } File [changed]: JumpList.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/JumpList.java?r1=1.14&r2=1.15 Delta lines: +17 -296 ---------------------- --- JumpList.java 30 Jun 2006 19:17:52 -0000 1.14 +++ JumpList.java 19 Jan 2007 05:21:31 -0000 1.15 @@ -20,13 +20,9 @@ package org.netbeans.editor; import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.text.Document; import javax.swing.text.JTextComponent; -import javax.swing.text.BadLocationException; +import org.netbeans.modules.editor.lib2.DocumentsJumpList; +import org.openide.util.WeakListeners; /** * The last several jumps in either the current file @@ -35,80 +31,33 @@ * * @author Miloslav Metelka * @version 1.00 +* @deprecated Without any replacement. */ public class JumpList { - /** Maximum size to which the list will be shrinked - * if it exceeds the THRESHOLD_SIZE. - */ - private static final int MAX_SIZE = 50; - - /** Reaching this count means that the size should be checked - * and possibly shrinked to the MAX_SIZE. - */ - private static final int CHECK_COUNT = 10; - - /** Current jump list entry */ - private static Entry currentEntry; - - private static int checkCnt; - private static final WeakPropertyChangeSupport support = new WeakPropertyChangeSupport(); - private static boolean dotAtCurrentEntry = false; - - private static ChangeListener registryListener = new ChangeListener() { - public void stateChanged(ChangeEvent e) { - support.firePropertyChange(JumpList.class, null, null, null); - } - }; - - static { - Registry.addChangeListener(registryListener); - } - static void addPropertyChangeListener(PropertyChangeListener listener) { - support.addPropertyChangeListener(listener); + PropertyChangeListener pcl = WeakListeners.propertyChange(listener, null); + DocumentsJumpList.addPropertyChangeListener(pcl); } static void dotMoved(JTextComponent c, int offset) { - if (dotAtCurrentEntry && currentEntry != null && (currentEntry.getComponent() != c || currentEntry.getPosition() != offset)) { - support.firePropertyChange(JumpList.class, null, null, null); - dotAtCurrentEntry = false; - } else { - dotAtCurrentEntry = currentEntry != null && currentEntry.getComponent() == c && currentEntry.getPosition() == offset; - } + DocumentsJumpList.dotMoved(c, offset); } public static void checkAddEntry() { - JTextComponent c = Utilities.getLastActiveComponent(); - if (c != null) { - checkAddEntry(c, c.getCaret().getDot()); - } + DocumentsJumpList.checkAddEntry(); } public static void checkAddEntry(JTextComponent c) { - checkAddEntry(c, c.getCaret().getDot()); + DocumentsJumpList.checkAddEntry(c); } public static void checkAddEntry(JTextComponent c, int pos) { - if (currentEntry == null - || currentEntry.getComponent() != c - || currentEntry.getPosition() != pos - ) { - addEntry(c, pos); - } + DocumentsJumpList.checkAddEntry(c, pos); } public static void addEntry(JTextComponent c, int pos) { - try { - Entry e = new Entry(c, pos, currentEntry); - currentEntry = e; - if (++checkCnt >= CHECK_COUNT) { // perform size check - sizeCheck(); - } - dotAtCurrentEntry = true; - } catch (BadLocationException e) { - // entry not added - } + DocumentsJumpList.addEntry(c, pos); } /** @@ -116,49 +65,11 @@ * component and position with those stored in the entries. */ public static void jumpPrev(JTextComponent c) { - int dotPos = c.getCaret().getDot(); - if (currentEntry != null) { - while (true) { - int entryPos = currentEntry.getPosition(); - JTextComponent entryComp = currentEntry.getComponent(); - if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { - if (currentEntry.setDot()) { - support.firePropertyChange(JumpList.class, null, null, null); - break; - } - } - if (currentEntry.prev != null) { // must check not to end up with null - currentEntry = currentEntry.prev; - } else { - break; // break when on the last entry - } - } - } + DocumentsJumpList.jumpPrev(c); } public static boolean hasPrev() { - JTextComponent c = Utilities.getLastActiveComponent(); - if (c != null) { - int dotPos = c.getCaret().getDot(); - Entry e = currentEntry; - if (e != null) { - while (true) { - int entryPos = e.getPosition(); - JTextComponent entryComp = e.getComponent(); - if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { - if (entryPos >= 0 && entryPos <= entryComp.getDocument().getLength()) { - return true; - } - } - if (e.prev != null) { // must check not to end up with null - e = e.prev; - } else { - break; // break when on the last entry - } - } - } - } - return false; + return DocumentsJumpList.hasPrev(); } /** @@ -166,51 +77,11 @@ * component and position with those stored in the entries. */ public static void jumpNext(JTextComponent c) { - int dotPos = c.getCaret().getDot(); - if (currentEntry != null) - currentEntry = currentEntry.next; - if (currentEntry != null) { - while (true) { - int entryPos = currentEntry.getPosition(); - JTextComponent entryComp = currentEntry.getComponent(); - if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { - if (currentEntry.setDot()) { - support.firePropertyChange(JumpList.class, null, null, null); - break; - } - } - if (currentEntry.next != null) { // must check not to end up with null - currentEntry = currentEntry.next; - } else { - break; // break when on the last entry - } - } - } + DocumentsJumpList.jumpNext(c); } public static boolean hasNext() { - JTextComponent c = Utilities.getLastActiveComponent(); - if (c != null) { - int dotPos = c.getCaret().getDot(); - Entry e = currentEntry != null ? currentEntry.next : currentEntry; - if (e != null) { - while (true) { - int entryPos = e.getPosition(); - JTextComponent entryComp = e.getComponent(); - if (entryComp != null && (entryComp != c || (entryPos >= 0 && entryPos != dotPos))) { - if (entryPos >= 0 && entryPos <= entryComp.getDocument().getLength()) { - return true; - } - } - if (e.next!= null) { // must check not to end up with null - e = e.next; - } else { - break; // break when on the last entry - } - } - } - } - return false; + return DocumentsJumpList.hasNext(); } /** @@ -218,21 +89,7 @@ * component to those stored in the jump list entries. */ public static void jumpPrevComponent(JTextComponent c) { - if (currentEntry != null) { - while (true) { - JTextComponent entryComp = currentEntry.getComponent(); - if (entryComp != null && entryComp != c) { - if (currentEntry.setDot()) { - break; - } - } - if (currentEntry.prev != null) { // must check not to end up with null - currentEntry = currentEntry.prev; - } else { - break; // break when on the last entry - } - } - } + DocumentsJumpList.jumpPrevComponent(c); } /** @@ -240,146 +97,10 @@ * component to those stored in the jump list entries. */ public static void jumpNextComponent(JTextComponent c) { - if (currentEntry != null) { - while (true) { - JTextComponent entryComp = currentEntry.getComponent(); - if (entryComp != null && entryComp != c) { - if (currentEntry.setDot()) { - break; - } - } - if (currentEntry.next != null) { // must check not to end up with null - currentEntry = currentEntry.next; - } else { - break; // break when on the last entry - } - } - } + DocumentsJumpList.jumpNextComponent(c); } public static String dump() { - StringBuffer sb = new StringBuffer(); - int i = 0; - Entry e = currentEntry; - if (e != null) { - while (true) { - if (e.prev != null) { - e = e.prev; - i--; - } else { - break; + return DocumentsJumpList.dump(); } - } - - while (e != null) { - JTextComponent comp = e.getComponent(); - String docStr = (comp != null) ? - (String)comp.getDocument().getProperty(Document.TitleProperty) - : ""; // NOI18N - if (docStr == null) { // no title property - docStr = "Untitled"; // NOI18N - } - sb.append("[" + i++ + "]=" + docStr + ", " + e.getPosition() + "\n"); // NOI18N - e = e.next; - } - } else { // null current entry - sb.append("Empty list"); // NOI18N - } - return sb.toString(); - } - - private static void sizeCheck() { - int cnt = MAX_SIZE; - Entry e = currentEntry; - while (e != null && cnt > 0) { - e = e.prev; - cnt--; // #19429 - } - if (e != null) { // reached the one that should be the first - e.makeFirst(); - } - } - - public static class Entry { - - /** ID of the stored position component */ - private int componentID; - - /** ID of the position stored in the document */ - private int posID; - - /** Previous entry in the linked list */ - Entry prev; - - /** Next entry in the linked list */ - Entry next; - - Entry(JTextComponent component, int offset, Entry last) throws BadLocationException { - componentID = Registry.getID(component); - posID = ((BaseDocument)component.getDocument()).storePosition(offset); - if (last != null) { // apend after the last entry - last.next = this; - this.prev = last; - } - } - - public int getPosition() { - JTextComponent c = Registry.getComponent(componentID); - int pos = -1; - if (c != null) { - pos = ((BaseDocument)c.getDocument()).getStoredPosition(posID); - } - return pos; - } - - public JTextComponent getComponent() { - return Registry.getComponent(componentID); - } - - /** Set the dot to the component and position - * stored in the mark. - * @return true if the caret was successfully moved - */ - public boolean setDot() { - JTextComponent c = getComponent(); - if (c != null) { - if (Utilities.getLastActiveComponent() != c) { - - Utilities.requestFocus(c); // possibly request for the component - Registry.activate(c); - } - - int pos = getPosition(); - if (pos >= 0 && pos <= c.getDocument().getLength()) { - c.getCaret().setDot(pos); // set the dot - return true; - } - } - return false; - } - - void makeLast() { - if (next != null) { - next.prev = null; - next = null; - } - } - - void makeFirst() { - if (prev != null) { - prev.next = null; - prev = null; - } - } - - protected void finalize() throws Throwable { - JTextComponent c = Registry.getComponent(componentID); - if (c != null) { - ((BaseDocument)c.getDocument()).removeStoredPosition(posID); - } - super.finalize(); - } - - } - } File [changed]: MarkFactory.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/MarkFactory.java?r1=1.25&r2=1.26 Delta lines: +0 -12 -------------------- --- MarkFactory.java 30 Jun 2006 19:17:53 -0000 1.25 +++ MarkFactory.java 19 Jan 2007 05:21:31 -0000 1.26 @@ -274,16 +274,4 @@ } } - - /** Special mark for caret. Its signature is used in Analyzer not to move - * this mark when initial read is performed. - */ - static class CaretMark extends DrawMark { - - CaretMark() { - super(DrawLayerFactory.CARET_LAYER_NAME, null); - } - - } - } File [changed]: Registry.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/Registry.java?r1=1.16&r2=1.17 Delta lines: +50 -358 ---------------------- --- Registry.java 30 Jun 2006 19:17:54 -0000 1.16 +++ Registry.java 19 Jan 2007 05:21:31 -0000 1.17 @@ -19,13 +19,12 @@ package org.netbeans.editor; -import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.Iterator; -import java.util.Enumeration; import javax.swing.text.JTextComponent; import javax.swing.event.ChangeListener; -import javax.swing.event.ChangeEvent; +import javax.swing.text.Document; +import org.netbeans.modules.editor.lib2.DocumentsRegistry; +import org.openide.util.WeakListeners; /** * All the documents and components register here so that @@ -35,35 +34,10 @@ * * @author Miloslav Metelka * @version 1.00 +* @deprecated Without any replacement. */ public class Registry { - private static final WeakReference[] EMPTY = new WeakReference[0]; - - /** Array of weak references to documents */ - private static WeakReference[] docRefs = EMPTY; - - /** Number of the document references */ - private static int docRefsCount; - - /** Array of activated document numbers */ - private static final ArrayList docAct = new ArrayList(); - - /** Array list of weak references to components */ - private static WeakReference[] compRefs = EMPTY; - - /** Number of the document references */ - private static int compRefsCount; - - /** Array of activated component numbers */ - private static final ArrayList compAct = new ArrayList(); - - /** List of the registered changes listeners */ - private static final WeakEventListenerList listenerList - = new WeakEventListenerList(); - - private static int consolidateCounter; - /** Add weak listener to listen to change of activity of documents or components. * The caller must * hold the listener object in some instance variable to prevent it @@ -71,7 +45,7 @@ * @param l listener to add */ public static void addChangeListener(ChangeListener l) { - listenerList.add(ChangeListener.class, l); + DocumentsRegistry.addChangeListener(WeakListeners.change(l, null)); } /** Remove listener for changes in activity. It's optional @@ -80,24 +54,23 @@ * @param l listener to remove */ public static void removeChangeListener(ChangeListener l) { - listenerList.remove(ChangeListener.class, l); + // no-op } /** Get document ID from the document. * @return document id or -1 if document was not yet added to the registry * by addDocument(). */ - public static synchronized int getID(BaseDocument doc) { - Integer i = getIDInteger(doc); - return (i != null) ? i.intValue() : -1; + public static int getID(BaseDocument doc) { + return DocumentsRegistry.getID(doc); } /** Get component ID from the component. * @return component id or -1 if component was not yet added to the registry * by addComponent(). */ - public static synchronized int getID(JTextComponent c) { - return getIDImpl(c); + public static int getID(JTextComponent c) { + return DocumentsRegistry.getID(c); } /** Get document when its ID is known. @@ -106,13 +79,10 @@ * by getID(doc). * @return document instance or null when document no longer exists */ - public static synchronized BaseDocument getDocument(int docID) { - if (docID < 0 || docID >= docRefsCount) { - return null; - } - - WeakReference wr = docRefs[docID]; - return (wr != null) ? (BaseDocument)wr.get() : null; + public static BaseDocument getDocument(int docID) { + // TODO: wrap non-BaseDocuments somehow + Document doc = DocumentsRegistry.getDocument(docID); + return doc instanceof BaseDocument ? (BaseDocument) doc : null; } /** Get component when its ID is known. @@ -121,32 +91,16 @@ * by getID(c). * @return component instance or null when document no longer exists */ - public static synchronized JTextComponent getComponent(int compID) { - if (compID < 0 || compID >= compRefsCount) { - return null; - } - - WeakReference wr = compRefs[compID]; - return (wr != null) ? (JTextComponent)wr.get() : null; + public static JTextComponent getComponent(int compID) { + return DocumentsRegistry.getComponent(compID); } /** Add document to registry. Doesn't search for repetitive * adding. * @return registry unique ID of the document */ - public static synchronized int addDocument(BaseDocument doc) { - Integer docID = getIDInteger(doc); - if (docID != null) { // already added - return docID.intValue(); - } - - if (docRefsCount >= docRefs.length) { - docRefs = realloc(docRefs); - } - - docRefs[docRefsCount] = new WeakReference(doc); - doc.putProperty(BaseDocument.ID_PROP, new Integer(docRefsCount)); - return docRefsCount++; + public static int addDocument(BaseDocument doc) { + return DocumentsRegistry.addDocument(doc); } /** Add component to registry. If the component is already registered @@ -155,18 +109,7 @@ * @return ID of the component */ public static synchronized int addComponent(JTextComponent c) { - int compID = getIDImpl(c); - if (compID != -1) { - return compID; // already registered - } - - if (compRefsCount >= compRefs.length) { - compRefs = realloc(compRefs); - } - - compRefs[compRefsCount] = new WeakReference(c); - ((BaseTextUI)c.getUI()).componentID = compRefsCount; - return compRefsCount++; + return DocumentsRegistry.addComponent(c); } /** Remove component from registry. It's usually done when @@ -176,66 +119,14 @@ * if the component was not yet added to the registry. */ public static synchronized int removeComponent(JTextComponent c) { - int compID = getIDImpl(c); - - if (compID != -1) { - compRefs[compID] = null; - // Search whether was activated - for (int i = compAct.size() - 1; i >= 0; i--) { - if (((Integer)compAct.get(i)).intValue() == compID) { - compAct.remove(i); - break; - } - } - } - - return compID; + return DocumentsRegistry.removeComponent(c); } /** Put the component to the first position in the array of last accessed * components. The activate of document is also called automatically. */ public static void activate(JTextComponent c) { - boolean activated = true; - synchronized (Registry.class) { - int compID = getIDImpl(c); - if (compID == -1) { // c not registered - return; - } - - int actSize = compAct.size(); - int ind = 0; - while (ind < actSize) { - int id = ((Integer)compAct.get(ind)).intValue(); - if (id == compID) { // found - if (ind == 0) { - break; - } - compAct.add(0, compAct.remove(ind)); - activated = true; - break; - } - - ind++; - } - - if (ind == actSize) { - compAct.add(0, new Integer(compID)); - activated = true; - } - - // Try to activate component's document too - Object doc = c.getDocument(); - if (doc instanceof BaseDocument) { - if (doActivate((BaseDocument)doc)) { - activated = true; - } - } - } - - if (activated) { - fireChange(); - } + DocumentsRegistry.activate(c); } /** Put the document to the first position in the array of last accessed @@ -244,39 +135,43 @@ * @param doc document to be activated */ public static void activate(BaseDocument doc) { - boolean activated; - synchronized (Registry.class) { - activated = doActivate(doc); - } - - if (activated) { - fireChange(); - } + DocumentsRegistry.activate(doc); } public static synchronized BaseDocument getMostActiveDocument() { - return getValidDoc(0, true); + // TODO: wrap non-BaseDocuments somehow + Document doc = DocumentsRegistry.getMostActiveDocument(); + return doc instanceof BaseDocument ? (BaseDocument) doc : null; } public static synchronized BaseDocument getLeastActiveDocument() { - int lastInd = docAct.size() - 1; - return getValidDoc(lastInd, false); + // TODO: wrap non-BaseDocuments somehow + Document doc = DocumentsRegistry.getLeastActiveDocument(); + return doc instanceof BaseDocument ? (BaseDocument) doc : null; } public static BaseDocument getLessActiveDocument(BaseDocument doc) { - return getLessActiveDocument(getID(doc)); + // TODO: wrap non-BaseDocuments somehow + Document doc2 = DocumentsRegistry.getLessActiveDocument(doc); + return doc2 instanceof BaseDocument ? (BaseDocument) doc2 : null; } public static synchronized BaseDocument getLessActiveDocument(int docID) { - return getNextActiveDoc(docID, true); + // TODO: wrap non-BaseDocuments somehow + Document doc2 = DocumentsRegistry.getLessActiveDocument(docID); + return doc2 instanceof BaseDocument ? (BaseDocument) doc2 : null; } public static BaseDocument getMoreActiveDocument(BaseDocument doc) { - return getMoreActiveDocument(getID(doc)); + // TODO: wrap non-BaseDocuments somehow + Document doc2 = DocumentsRegistry.getMoreActiveDocument(doc); + return doc2 instanceof BaseDocument ? (BaseDocument) doc2 : null; } public static synchronized BaseDocument getMoreActiveDocument(int docID) { - return getNextActiveDoc(docID, false); + // TODO: wrap non-BaseDocuments somehow + Document doc2 = DocumentsRegistry.getMoreActiveDocument(docID); + return doc2 instanceof BaseDocument ? (BaseDocument) doc2 : null; } /** Get the iterator over the active documents. It starts with @@ -285,246 +180,43 @@ * not reflect future changes. */ public static synchronized Iterator getDocumentIterator() { - consolidate(); - - ArrayList docList = new ArrayList(); - int actSize = docAct.size(); - for (int i = 0; i < actSize; i++) { - int ind = ((Integer)docAct.get(i)).intValue(); - WeakReference wr = docRefs[ind]; - if (wr != null) { - Object doc = wr.get(); - if (doc != null) { - docList.add(doc); - } - } - } - - return docList.iterator(); + // TODO: wrap non-BaseDocuments somehow + return DocumentsRegistry.getDocumentIterator(); } public static synchronized JTextComponent getMostActiveComponent() { - return getValidComp(0, true); + return DocumentsRegistry.getMostActiveComponent(); } public static synchronized JTextComponent getLeastActiveComponent() { - int lastInd = compAct.size() - 1; - return getValidComp(lastInd, false); + return DocumentsRegistry.getLeastActiveComponent(); } public static JTextComponent getLessActiveComponent(JTextComponent c) { - return getLessActiveComponent(getID(c)); + return DocumentsRegistry.getLessActiveComponent(c); } public static synchronized JTextComponent getLessActiveComponent(int compID) { - return getNextActiveComp(compID, true); + return DocumentsRegistry.getLessActiveComponent(compID); } public static JTextComponent getMoreActiveComponent(JTextComponent c) { - return getMoreActiveComponent(getID(c)); + return DocumentsRegistry.getMoreActiveComponent(c); } public static synchronized JTextComponent getMoreActiveComponent(int compID) { - return getNextActiveComp(compID, false); + return DocumentsRegistry.getMoreActiveComponent(compID); } /** Get the iterator over the active components. It starts with * the most active component till the least active component. */ public static synchronized Iterator getComponentIterator() { - consolidate(); - - ArrayList compList = new ArrayList(); - int actSize = compAct.size(); - for (int i = 0; i < actSize; i++) { - int ind = ((Integer)compAct.get(i)).intValue(); - WeakReference wr = compRefs[ind]; - if (wr != null) { - Object comp = wr.get(); - if (comp != null) { - compList.add(comp); - } - } - } - - return compList.iterator(); - } - - private static WeakReference[] realloc(WeakReference[] refs) { - WeakReference[] tmp = new WeakReference[refs.length * 2 + 4]; - System.arraycopy(refs, 0, tmp, 0, refs.length); - return tmp; - } - - private static void consolidate() { - while (++consolidateCounter >= 20) { // after every 20th call - consolidateCounter = 0; - - // Remove empty document references - for (int i = docAct.size() - 1; i >= 0; i--) { - int ind = ((Integer)docAct.get(i)).intValue(); - WeakReference wr = docRefs[ind]; - if (wr != null) { - if (wr.get() == null) { // empty reference - docAct.remove(i); - docRefs[ind] = null; - } - } - } - - // Remove empty component references - for (int i = compAct.size() - 1; i >= 0; i--) { - int ind = ((Integer)compAct.get(i)).intValue(); - WeakReference wr = compRefs[ind]; - if (wr != null) { - if (wr.get() == null) { // empty reference - compAct.remove(i); - compRefs[ind] = null; - } - } - } - } - } - - private static int getIDImpl(JTextComponent c) { - if (c == null) { - return -1; - } - return ((BaseTextUI)c.getUI()).componentID; - } - - private static Integer getIDInteger(BaseDocument doc) { - if (doc == null) { - return null; - } - - return (Integer)doc.getProperty(BaseDocument.ID_PROP); - } - - private static boolean doActivate(BaseDocument doc) { - Integer docIDInteger = getIDInteger(doc); - - if (docIDInteger == null) { - return false; // document not added to registry - } - - int docID = (docIDInteger != null) ? docIDInteger.intValue() : -1; - - int size = docAct.size(); - for (int ind = 0; ind < size; ind++) { - int id = ((Integer)docAct.get(ind)).intValue(); - if (id == docID) { - if (ind == 0) { // no change - return false; - } - - docAct.add(0, docAct.remove(ind)); - return true; - } - } - - docAct.add(0, docIDInteger); - return true; - } - - private static BaseDocument getValidDoc(int ind, boolean forward) { - consolidate(); - - int actSize = docAct.size(); - while (ind >= 0 && ind < actSize) { - int docID = ((Integer)docAct.get(ind)).intValue(); - WeakReference wr = docRefs[docID]; - BaseDocument doc = (wr != null) ? (BaseDocument)wr.get() : null; - if (doc != null) { - return doc; - } - ind += forward ? +1 : -1; - } - return null; - } - - private static BaseDocument getNextActiveDoc(int docID, boolean forward) { - consolidate(); - - int actSize = docAct.size(); - int ind = forward ? 0 : (actSize - 1); - while (ind >= 0 && ind < actSize) { - if (((Integer)docAct.get(ind)).intValue() == docID) { - ind += forward ? +1 : -1; // get next one - return getValidDoc(ind, forward); - } - ind += forward ? +1 : -1; - } - return null; - } - - private static JTextComponent getValidComp(int ind, boolean forward) { - consolidate(); - - int actSize = compAct.size(); - while (ind >= 0 && ind < actSize) { - int compID = ((Integer)compAct.get(ind)).intValue(); - WeakReference wr = compRefs[compID]; - JTextComponent c = (wr != null) ? (JTextComponent)wr.get() : null; - if (c != null) { - return c; - } - ind += forward ? +1 : -1; - } - return null; - } - - private static JTextComponent getNextActiveComp(int compID, boolean forward) { - int actSize = compAct.size(); - int ind = forward ? 0 : (actSize - 1); - while (ind >= 0 && ind < actSize) { - if (((Integer)compAct.get(ind)).intValue() == compID) { - ind += forward ? +1 : -1; - return getValidComp(ind, forward); - } - ind += forward ? +1 : -1; - } - return null; - } - - private static void fireChange() { - ChangeListener[] listeners - = (ChangeListener[])listenerList.getListeners(ChangeListener.class); - ChangeEvent evt = new ChangeEvent(Registry.class); - for (int i = 0; i < listeners.length; i++) { - listeners[i].stateChanged(evt); - } + return DocumentsRegistry.getComponentIterator(); } /** Debug the registry into string. */ public static synchronized String registryToString() { - StringBuffer sb = new StringBuffer(); - sb.append("Document References:\n"); // NOI18N - for (int i = 0; i < docRefsCount; i++) { - WeakReference wr = docRefs[i]; - sb.append("docRefs[" + i + "]=" + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N - } - sb.append("Component References:\n"); // NOI18N - for (int i = 0; i < compRefsCount; i++) { - WeakReference wr = (WeakReference)compRefs[i]; - sb.append("compRefs[" + i + "]=" + ((wr != null) ? wr.get() : "null") + "\n"); // NOI18N + return DocumentsRegistry.registryToString(); } - sb.append("\nActive Document Indexes:\n"); // NOI18N - for (int i = 0; i < docAct.size(); i++) { - sb.append(docAct.get(i)); - if (i != docAct.size() - 1) { - sb.append(", "); // NOI18N - } - } - sb.append("\nActive Component Indexes:\n"); // NOI18N - for (int i = 0; i < compAct.size(); i++) { - sb.append(compAct.get(i)); - if (i != compAct.size() - 1) { - sb.append(", "); // NOI18N - } - } - - return sb.toString(); - } - } Directory: /editor/libsrc/org/netbeans/editor/ext/ ================================================== File [changed]: ExtCaret.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ext/ExtCaret.java?r1=1.46&r2=1.47 Delta lines: +23 -83 --------------------- --- ExtCaret.java 22 Oct 2006 20:48:23 -0000 1.46 +++ ExtCaret.java 19 Jan 2007 05:21:32 -0000 1.47 @@ -53,10 +53,26 @@ public class ExtCaret extends BaseCaret { - /** Highlight row draw layer name */ + /** + * Highlight row draw layer name. + * + *

Using DrawLayers has been deprecated and this constant + * has no longer any meaning. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static final String HIGHLIGHT_ROW_LAYER_NAME = "highlight-row-layer"; // NOI18N - /** Highlight row draw layer visibility */ + /** + * Highlight row draw layer visibility. + * + *

Using DrawLayers has been deprecated and this constant + * has no longer any meaning. + * + * @deprecated Please use Highlighting SPI instead, for details see + * Editor Library 2. + */ public static final int HIGHLIGHT_ROW_LAYER_VISIBILITY = 2050; /** Highlight matching brace draw layer name */ @@ -65,23 +81,12 @@ /** Highlight matching brace draw layer visibility */ public static final int HIGHLIGHT_BRACE_LAYER_VISIBILITY = 11000; - /** Whether to highlight the background of the row - * where the caret is. - */ - boolean highlightRow; - /** Whether to hightlight the matching brace */ boolean highlightBrace; - /** Coloring used for highlighting the row where the caret is. */ - Coloring highlightRowColoring; - /** Coloring used for highlighting the matching brace */ Coloring highlightBraceColoring; - /** Mark holding the start of the line where the caret currently is. */ - MarkFactory.DrawMark highlightRowMark; - /** Mark holding the starting position of the matching brace. */ MarkFactory.DrawMark highlightBraceStartMark; @@ -113,14 +118,6 @@ protected void modelChanged(BaseDocument oldDoc, BaseDocument newDoc) { // Fix for #7108 braceMarksValid = false; // brace marks are out of date - new document - if (highlightRowMark != null) { - try { - highlightRowMark.remove(); - } catch (InvalidMarkException e) { - } - highlightRowMark = null; - } - if (highlightBraceStartMark != null) { try { highlightBraceStartMark.remove(); @@ -150,26 +147,9 @@ EditorUI editorUI = Utilities.getEditorUI(c); Class kitClass = Utilities.getKitClass(c); - highlightRowColoring = editorUI.getColoring( - ExtSettingsNames.HIGHLIGHT_CARET_ROW_COLORING); highlightBraceColoring = editorUI.getColoring( ExtSettingsNames.HIGHLIGHT_MATCH_BRACE_COLORING); - // Handle highlight row - boolean oldHighlightRow = highlightRow; - highlightRow = SettingsUtil.getBoolean(kitClass, - ExtSettingsNames.HIGHLIGHT_CARET_ROW, - ExtSettingsDefaults.defaultHighlightCaretRow); - - if (oldHighlightRow && !highlightRow && highlightRowMark != null) { - try { - highlightRowMark.remove(); - } catch (InvalidMarkException e) { - } - - highlightRowMark = null; - } - highlightBrace = SettingsUtil.getBoolean(kitClass, ExtSettingsNames.HIGHLIGHT_MATCH_BRACE, ExtSettingsDefaults.defaultHighlightMatchBrace); @@ -225,14 +205,12 @@ public void install(JTextComponent c) { EditorUI editorUI = Utilities.getEditorUI(c); - editorUI.addLayer(new HighlightRowLayer(), HIGHLIGHT_ROW_LAYER_VISIBILITY); editorUI.addLayer(new HighlightBraceLayer(), HIGHLIGHT_BRACE_LAYER_VISIBILITY); super.install(c); } public void deinstall(JTextComponent c) { EditorUI editorUI = Utilities.getEditorUI(c); - editorUI.removeLayer(HIGHLIGHT_ROW_LAYER_NAME); editorUI.removeLayer(HIGHLIGHT_BRACE_LAYER_NAME); super.deinstall(c); } @@ -298,35 +276,6 @@ } protected void update(boolean scrollViewToCaret) { - if (highlightRow) { // highlight row with the caret - JTextComponent c = component; - if (c != null) { - EditorUI editorUI = Utilities.getEditorUI(c); - BaseDocument doc = (BaseDocument)c.getDocument(); - int dotPos = getDot(); - try { - int bolPos = Utilities.getRowStart(doc, dotPos); - if (highlightRowMark != null) { - int markPos = highlightRowMark.getOffset(); - if (bolPos != markPos) { - editorUI.repaintOffset(markPos); - Utilities.moveMark(doc, highlightRowMark, bolPos); - editorUI.repaintOffset(bolPos); - } - } else { // highlight mark is null - highlightRowMark = new MarkFactory.DrawMark(HIGHLIGHT_ROW_LAYER_NAME, editorUI); - highlightRowMark.setActivateLayer(true); - Utilities.insertMark(doc, highlightRowMark, bolPos); - editorUI.repaintOffset(bolPos); - } - } catch (BadLocationException e) { - highlightRow = false; - } catch (InvalidMarkException e) { - highlightRow = false; - } - } - } - if (highlightBrace) { if (matchBraceUpdateSync || braceTimer == null) { updateMatchBrace(); @@ -374,20 +323,11 @@ } } - /** Draw layer to highlight the row where the caret currently resides */ - class HighlightRowLayer extends DrawLayerFactory.ColorLineLayer { - - public HighlightRowLayer() { - super(HIGHLIGHT_ROW_LAYER_NAME); - } - - protected Coloring getColoring(DrawContext ctx) { - return highlightRowColoring; - } - - } - - /** Draw layer to highlight the matching brace */ + /** + * Draw layer to highlight the matching brace. + * + * XXX: The HighlightBraceLayer needs to be rewritten using the new Highlighting SPI. + */ class HighlightBraceLayer extends DrawLayer.AbstractLayer { public HighlightBraceLayer() { File [changed]: ExtFinderFactory.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ext/ExtFinderFactory.java?r1=1.10&r2=1.11 Delta lines: +0 -1 ------------------- --- ExtFinderFactory.java 30 Jun 2006 19:17:58 -0000 1.10 +++ ExtFinderFactory.java 19 Jan 2007 05:21:32 -0000 1.11 @@ -21,7 +21,6 @@ import javax.swing.text.BadLocationException; import org.netbeans.editor.Analyzer; -import org.netbeans.editor.Finder; import org.netbeans.editor.FinderFactory; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Utilities; File [changed]: FindDialogPanel.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ext/FindDialogPanel.java?r1=1.16&r2=1.17 Delta lines: +1 -0 ------------------- --- FindDialogPanel.java 30 Jun 2006 19:17:59 -0000 1.16 +++ FindDialogPanel.java 19 Jan 2007 05:21:32 -0000 1.17 @@ -30,6 +30,7 @@ * * @author Miloslav Metelka, Petr Nejedly * @version 1.0 + * @deprecated Without any replacement. */ public class FindDialogPanel extends javax.swing.JPanel { File [changed]: FindDialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ext/FindDialogSupport.java?r1=1.75&r2=1.76 Delta lines: +14 -13 --------------------- --- FindDialogSupport.java 20 Oct 2006 02:01:08 -0000 1.75 +++ FindDialogSupport.java 19 Jan 2007 05:21:32 -0000 1.76 @@ -57,6 +57,7 @@ * * @author Miloslav Metelka * @version 1.00 +* @deprecated Without any replacement. */ public class FindDialogSupport extends WindowAdapter implements ActionListener { @@ -223,19 +224,19 @@ c.select(bsStart, bsEnd); } }else{ - EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); - DrawLayerFactory.IncSearchLayer incLayer - = (DrawLayerFactory.IncSearchLayer)editorUI.findLayer( - DrawLayerFactory.INC_SEARCH_LAYER_NAME); - if (incLayer != null) { - if (incLayer.isEnabled()) { - int offs = incLayer.getOffset(); - int len = incLayer.getLength(); - if (len > 0){ - c.select(offs, offs + len); - } - } - } +// EditorUI editorUI = ((BaseTextUI)c.getUI()).getEditorUI(); +// DrawLayerFactory.IncSearchLayer incLayer +// = (DrawLayerFactory.IncSearchLayer)editorUI.findLayer( +// DrawLayerFactory.INC_SEARCH_LAYER_NAME); +// if (incLayer != null) { +// if (incLayer.isEnabled()) { +// int offs = incLayer.getOffset(); +// int len = incLayer.getLength(); +// if (len > 0){ +// c.select(offs, offs + len); +// } +// } +// } } } FindSupport.getFindSupport().incSearchReset(); File [changed]: GotoDialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/libsrc/org/netbeans/editor/ext/GotoDialogSupport.java?r1=1.25&r2=1.26 Delta lines: +0 -14 -------------------- --- GotoDialogSupport.java 30 Jun 2006 19:17:59 -0000 1.25 +++ GotoDialogSupport.java 19 Jan 2007 05:21:32 -0000 1.26 @@ -20,37 +20,23 @@ package org.netbeans.editor.ext; import java.awt.*; -import java.awt.BorderLayout; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowAdapter; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; -import java.awt.event.ItemListener; -import java.awt.event.ItemEvent; -import java.util.Map; -import java.util.Hashtable; import java.util.ResourceBundle; -import javax.swing.JPanel; import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JComponent; -import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.Action; import javax.swing.text.Caret; import javax.swing.text.JTextComponent; -import org.netbeans.editor.BaseCaret; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.BaseKit; import org.netbeans.editor.Utilities; -import org.netbeans.editor.FindSupport; import org.netbeans.editor.DialogSupport; import org.netbeans.editor.EditorState; -import org.netbeans.editor.EditorUI; import org.openide.util.NbBundle; /** Directory: /editor/nbproject/ ============================= File [changed]: project.xml Url: http://editor.netbeans.org/source/browse/editor/nbproject/project.xml?r1=1.31&r2=1.32 Delta lines: +10 -1 -------------------- --- project.xml 16 Jan 2007 02:22:25 -0000 1.31 +++ project.xml 19 Jan 2007 05:21:32 -0000 1.32 @@ -33,6 +33,15 @@ + org.netbeans.modules.editor.lib2 + + + + 1 + + + + org.netbeans.modules.editor.mimelookup @@ -277,7 +286,7 @@ - Directory: /editor/options/src/org/netbeans/modules/options/colors/ =================================================================== File [changed]: ColorModel.java Url: http://editor.netbeans.org/source/browse/editor/options/src/org/netbeans/modules/options/colors/ColorModel.java?r1=1.29&r2=1.30 Delta lines: +13 -0 -------------------- --- ColorModel.java 12 Jan 2007 00:37:52 -0000 1.29 +++ ColorModel.java 19 Jan 2007 05:21:33 -0000 1.30 @@ -369,11 +369,24 @@ */ private void updateMimeType (String language) { String internalMimeType = languageToInternalMimeType(language, true); + + // XXX: There is several hacks in the few lines of code below. + // First, the 'mimeType' property on a Document is abused for + // injecting the name of a profile used for previewing changes in + // colors. This by itself causes several problems in other parts of + // the IDE that had to be worked around. Second, Document properties + // are normally not supposed to be changed during a lifetime of a Document + // and there is no way how to listen for those changes. Which means + // that we have to fire a property change on the JTextComponent containing + // the Document, so that the layers can get recalculated. + Document document = editorPane.getDocument (); document.putProperty ("mimeType", internalMimeType); editorPane.setEditorKit (CloneableEditorSupport.getEditorKit(internalMimeType)); document = editorPane.getDocument (); document.putProperty ("mimeType", internalMimeType); + editorPane.firePropertyChange(null, 0, 1); + editorPane.addCaretListener (new CaretListener () { public void caretUpdate (CaretEvent e) { int position = e.getDot (); Directory: /editor/settings/nbproject/ ====================================== File [changed]: project.xml Url: http://editor.netbeans.org/source/browse/editor/settings/nbproject/project.xml?r1=1.3&r2=1.4 Delta lines: +1 -35 -------------------- --- project.xml 30 Jun 2006 19:18:21 -0000 1.3 +++ project.xml 19 Jan 2007 05:21:33 -0000 1.4 @@ -22,41 +22,7 @@ org.netbeans.modules.editor.settings - - - org.netbeans.modules.editor.mimelookup - - - - 1 - 1.0 - - - - org.openide.filesystems - - - - 6.2 - - - - org.openide.modules - - - - 6.2 - - - - org.openide.util - - - - 6.2 - - - + org.netbeans.api.editor.settings Directory: /editor/settings/src/org/netbeans/api/editor/settings/ ================================================================= File [changed]: FontColorNames.java Url: http://editor.netbeans.org/source/browse/editor/settings/src/org/netbeans/api/editor/settings/FontColorNames.java?r1=1.4&r2=1.5 Delta lines: +4 -1 ------------------- --- FontColorNames.java 6 Nov 2006 04:46:06 -0000 1.4 +++ FontColorNames.java 19 Jan 2007 05:21:33 -0000 1.5 @@ -64,6 +64,9 @@ /** Coloring used to mark important text in the status bar */ public static final String STATUS_BAR_BOLD_COLORING = "status-bar-bold"; // NOI18N + /** Coloring used to highlight the row where the caret resides */ + public static final String CARET_ROW_COLORING = "highlight-caret-row"; // NOI18N + private FontColorNames() { // to prevent instantialization } Directory: /editor/src/META-INF/services/ ========================================= File [added]: org.netbeans.spi.editor.DialogFactory Url: http://editor.netbeans.org/source/browse/editor/src/META-INF/services/org.netbeans.spi.editor.DialogFactory?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- org.netbeans.modules.editor.impl.NbDialogFactory File [added]: org.netbeans.spi.editor.EditorImplementationProvider Url: http://editor.netbeans.org/source/browse/editor/src/META-INF/services/org.netbeans.spi.editor.EditorImplementationProvider?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 1 -------------- org.netbeans.modules.editor.impl.NbEditorImplementationProvider Directory: /editor/src/org/netbeans/modules/editor/ =================================================== File [changed]: EditorModule.java Url: http://editor.netbeans.org/source/browse/editor/src/org/netbeans/modules/editor/EditorModule.java?r1=1.122&r2=1.123 Delta lines: +0 -5 ------------------- --- EditorModule.java 30 Aug 2006 08:45:09 -0000 1.122 +++ EditorModule.java 19 Jan 2007 05:21:34 -0000 1.123 @@ -91,11 +91,6 @@ LocaleSupport.addLocalizer(new NbLocalizer(AllOptions.class)); LocaleSupport.addLocalizer(new NbLocalizer(BaseKit.class)); - // Initializations - DialogSupport.setDialogFactory( new NbDialogSupport() ); - - ImplementationProvider.registerDefault(new NbImplementationProvider()); - // register loader for annotation types AnnotationTypes.getTypes().registerLoader( new AnnotationTypes.Loader() { public void loadTypes() { File [changed]: NbDialogSupport.java Url: http://editor.netbeans.org/source/browse/editor/src/org/netbeans/modules/editor/NbDialogSupport.java?r1=1.14&r2=1.15 Delta lines: +5 -88 -------------------- --- NbDialogSupport.java 30 Jun 2006 19:18:27 -0000 1.14 +++ NbDialogSupport.java 19 Jan 2007 05:21:34 -0000 1.15 @@ -19,102 +19,19 @@ package org.netbeans.modules.editor; -import java.awt.event.*; -import java.awt.Dialog; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JPanel; -import javax.swing.KeyStroke; import org.netbeans.editor.DialogSupport; -import org.openide.DialogDescriptor; -import org.openide.util.HelpCtx; -import java.util.HashMap; +import org.netbeans.modules.editor.impl.NbDialogFactory; /** The NetBeans way of handling Dialogs is through TopManager, * prividing it with DialogDescriptor. * * @author Petr Nejedly * @version 1.0 + * @deprecated Without any replacement. */ -public class NbDialogSupport implements DialogSupport.DialogFactory { +public class NbDialogSupport extends NbDialogFactory implements DialogSupport.DialogFactory { + public NbDialogSupport() { - /** - * Hash map containing string (ClassNames) <-> string (HelpID). - */ - private static HashMap helpIDs; - - private static final String HELP_ID_MacroSavePanel = "editing.macros.recording"; // !!! NOI18N - private static final String HELP_ID_FindPanel = "editing.find"; // !!! NOI18N - private static final String HELP_ID_JavaFastImportPanel = "editing.fastimport"; // !!! NOI18N - private static final String HELP_ID_ScrollCompletionPane = "editing.codecompletion"; // !!! NOI18N - - public NbDialogSupport() - { - if (helpIDs == null) - { - helpIDs = new HashMap(7); - helpIDs.put("org.netbeans.editor.MacroSavePanel", HELP_ID_MacroSavePanel); // NOI18N - helpIDs.put("org.netbeans.editor.ext.FindDialogSupport$FindPanel", HELP_ID_FindPanel); // NOI18N - helpIDs.put("org.netbeans.editor.ext.ScrollCompletionPane", HELP_ID_ScrollCompletionPane); // NOI18N - helpIDs.put("org.netbeans.editor.ext.java.JavaFastImportPanel", HELP_ID_JavaFastImportPanel); // NOI18N - } - } - - /** - * The method for creating a dialog with specified properties. - * @param title The title of created dialog. - * @param panel The content of the dialog to be displayed. - * @param modal Whether the dialog should be modal. - * @param buttons The array of JButtons to be added to the dialog. - * @param sideButtons The buttons could be placed under the panel (false), - * or on the right side of the panel (true). - * @param defaultIndex The index of default button in the buttons array, - * if index < 0, no default button is set. - * @param cancelIndex The index of cancel button - the button that will - * be pressed when closing the dialog.\ - * @param listener The listener which will be notified of all button - * events. - */ - public Dialog createDialog(String title, JPanel panel,boolean modal,JButton[] buttons,boolean sideButtons,int defaultIndex,int cancelIndex,ActionListener listener) { - String helpID = (String)helpIDs.get(panel.getClass().getName()); - Dialog d = org.openide.DialogDisplayer.getDefault().createDialog( - new DialogDescriptor( panel, title, modal, buttons, - defaultIndex == -1 ? buttons[0] : buttons[defaultIndex], - sideButtons ? DialogDescriptor.RIGHT_ALIGN : DialogDescriptor.BOTTOM_ALIGN, - helpID != null ? new HelpCtx( helpID ) : null, listener - ) - ); - - // register the cancel button helpers - if( cancelIndex >= 0 && d instanceof JDialog ) { - final JButton cancelButton = buttons[cancelIndex]; - // register the Esc key to simulate Cancel click - ((JDialog)d).getRootPane().registerKeyboardAction( - new ActionListener() { - public void actionPerformed(ActionEvent evt) { // l.actionPerformed( new ActionEvent(buttons[cancelButtonIndex], 0, null)); - cancelButton.doClick( 10 ); } - }, - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), - //JComponent.WHEN_IN_FOCUSED_WINDOW - JComponent.WHEN_FOCUSED - ); - - //bugfix of #45552, 45555, 45556, 45558 - ((JDialog)d).getRootPane().setFocusable(false); - - d.addWindowListener( - new WindowAdapter() { - public void windowClosing( WindowEvent evt ) { - cancelButton.doClick( 10 ); - } - } - ); - } - - return d; - } - } File [changed]: NbImplementationProvider.java Url: http://editor.netbeans.org/source/browse/editor/src/org/netbeans/modules/editor/NbImplementationProvider.java?r1=1.11&r2=1.12 Delta lines: +13 -34 --------------------- --- NbImplementationProvider.java 29 Nov 2006 01:51:31 -0000 1.11 +++ NbImplementationProvider.java 19 Jan 2007 05:21:34 -0000 1.12 @@ -24,8 +24,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; - -import org.openide.util.NbBundle; import org.netbeans.editor.ImplementationProvider; import javax.swing.*; @@ -33,59 +31,40 @@ import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.editor.BaseKit; -import org.netbeans.editor.Utilities; +import org.netbeans.modules.editor.impl.NbEditorImplementationProvider; import org.netbeans.modules.editor.options.BaseOptions; import org.openide.cookies.InstanceCookie; import org.openide.loaders.DataObject; -import org.openide.windows.TopComponent; - /** This is NetBeans specific provider of functionality. * See base class for detailed comments. * * @author David Konecny * @since 10/2001 + * @deprecated Without any replacement. */ - public class NbImplementationProvider extends ImplementationProvider { - public static final String GLYPH_GUTTER_ACTIONS_FOLDER_NAME = "GlyphGutterActions"; //NOI18N + public static final String GLYPH_GUTTER_ACTIONS_FOLDER_NAME = + NbEditorImplementationProvider.GLYPH_GUTTER_ACTIONS_FOLDER_NAME; + + private transient NbEditorImplementationProvider provider; + + public NbImplementationProvider() { + provider = new NbEditorImplementationProvider(); + } /** Ask NbBundle for the resource bundle */ public ResourceBundle getResourceBundle(String localizer) { - return NbBundle.getBundle(localizer); + return provider.getResourceBundle(localizer); } - public Action[] getGlyphGutterActions(JTextComponent target) { - Class kitClass = Utilities.getKitClass(target); - List retList = new ArrayList(); - List icList = getInstanceCookiesPerKitClass(kitClass); - try{ - for (int i = 0; i string (HelpID). */ private static HashMap helpIDs; private static final String HELP_ID_MacroSavePanel = "editing.macros.recording"; // !!! NOI18N private static final String HELP_ID_FindPanel = "editing.find"; // !!! NOI18N private static final String HELP_ID_JavaFastImportPanel = "editing.fastimport"; // !!! NOI18N private static final String HELP_ID_ScrollCompletionPane = "editing.codecompletion"; // !!! NOI18N public NbDialogFactory() { if (helpIDs == null) { helpIDs = new HashMap(7); helpIDs.put("org.netbeans.editor.MacroSavePanel", HELP_ID_MacroSavePanel); // NOI18N helpIDs.put("org.netbeans.editor.ext.FindDialogSupport$FindPanel", HELP_ID_FindPanel); // NOI18N helpIDs.put("org.netbeans.editor.ext.ScrollCompletionPane", HELP_ID_ScrollCompletionPane); // NOI18N helpIDs.put("org.netbeans.editor.ext.java.JavaFastImportPanel", HELP_ID_JavaFastImportPanel); // NOI18N } } /** * The method for creating a dialog with specified properties. * @param title The title of created dialog. * @param panel The content of the dialog to be displayed. * @param modal Whether the dialog should be modal. * @param buttons The array of JButtons to be added to the dialog. * @param sideButtons The buttons could be placed under the panel (false), * or on the right side of the panel (true). * @param defaultIndex The index of default button in the buttons array, * if index < 0, no default button is set. * @param cancelIndex The index of cancel button - the button that will * be pressed when closing the dialog.\ * @param listener The listener which will be notified of all button * events. */ public Dialog createDialog(String title, JPanel panel,boolean modal,JButton[] buttons,boolean sideButtons,int defaultIndex,int cancelIndex,ActionListener listener) { String helpID = (String)helpIDs.get(panel.getClass().getName()); Dialog d = org.openide.DialogDisplayer.getDefault().createDialog( new DialogDescriptor( panel, title, modal, buttons, defaultIndex == -1 ? buttons[0] : buttons[defaultIndex], sideButtons ? DialogDescriptor.RIGHT_ALIGN : DialogDescriptor.BOTTOM_ALIGN, helpID != null ? new HelpCtx( helpID ) : null, listener ) ); // register the cancel button helpers if( cancelIndex >= 0 && d instanceof JDialog ) { final JButton cancelButton = buttons[cancelIndex]; // register the Esc key to simulate Cancel click ((JDialog)d).getRootPane().registerKeyboardAction( new ActionListener() { public void actionPerformed(ActionEvent evt) { // l.actionPerformed( new ActionEvent(buttons[cancelButtonIndex], 0, null)); cancelButton.doClick( 10 ); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), //JComponent.WHEN_IN_FOCUSED_WINDOW JComponent.WHEN_FOCUSED ); //bugfix of #45552, 45555, 45556, 45558 ((JDialog)d).getRootPane().setFocusable(false); d.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { cancelButton.doClick( 10 ); } } ); } return d; } } File [added]: NbEditorImplementationProvider.java Url: http://editor.netbeans.org/source/browse/editor/src/org/netbeans/modules/editor/impl/NbEditorImplementationProvider.java?rev=1.2&content-type=text/vnd.viewcvs-markup Added lines: 122 ---------------- /* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.impl; import java.util.ResourceBundle; import java.awt.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.openide.util.NbBundle; import javax.swing.*; import javax.swing.text.JTextComponent; import org.netbeans.editor.BaseKit; import org.netbeans.editor.Utilities; import org.netbeans.modules.editor.options.BaseOptions; import org.netbeans.spi.editor.EditorImplementationProvider; import org.openide.cookies.InstanceCookie; import org.openide.loaders.DataObject; import org.openide.windows.TopComponent; /** * * @author Vita Stejskal */ public final class NbEditorImplementationProvider implements EditorImplementationProvider { public static final String GLYPH_GUTTER_ACTIONS_FOLDER_NAME = "GlyphGutterActions"; //NOI18N public NbEditorImplementationProvider() { } /** Ask NbBundle for the resource bundle */ public ResourceBundle getResourceBundle(String localizer) { return NbBundle.getBundle(localizer); } public Action[] getGlyphGutterActions(JTextComponent target) { Class kitClass = Utilities.getKitClass(target); List retList = new ArrayList(); List icList = getInstanceCookiesPerKitClass(kitClass); try{ for (int i = 0; i + File [changed]: properties.xml Url: http://nbbuild.netbeans.org/source/browse/nbbuild/javadoctools/properties.xml?r1=1.30&r2=1.31 Delta lines: +1 -0 ------------------- --- properties.xml 6 Dec 2006 10:20:32 -0000 1.30 +++ properties.xml 19 Jan 2007 05:21:35 -0000 1.31 @@ -117,3 +117,4 @@ + File [changed]: replaces.xml Url: http://nbbuild.netbeans.org/source/browse/nbbuild/javadoctools/replaces.xml?r1=1.31&r2=1.32 Delta lines: +1 -0 ------------------- --- replaces.xml 6 Dec 2006 10:20:32 -0000 1.31 +++ replaces.xml 19 Jan 2007 05:21:35 -0000 1.32 @@ -117,3 +117,4 @@ + Directory: /nbbuild/nbproject/ ============================== File [changed]: project.xml Url: http://nbbuild.netbeans.org/source/browse/nbbuild/nbproject/project.xml?r1=1.59&r2=1.60 Delta lines: +2 -1 ------------------- --- project.xml 2 Jan 2007 23:05:02 -0000 1.59 +++ project.xml 19 Jan 2007 05:21:35 -0000 1.60 @@ -227,6 +227,8 @@ ../editor/hints ../editor/hints/highlights ../editor/lib + ../editor/lib/bridge + ../editor/lib2 ../editor/mimelookup ../editor/mimelookup/impl ../editor/options @@ -293,7 +295,6 @@ ../javadoc ../junit ../lexer - ../lexer/editorbridge ../lexer/nbbridge ../libs/commons_logging ../libs/jsch Directory: /ide/golden/ ======================= File [changed]: cluster-deps.txt Url: http://ide.netbeans.org/source/browse/ide/golden/cluster-deps.txt?r1=1.73&r2=1.74 Delta lines: +0 -1 ------------------- --- cluster-deps.txt 5 Jan 2007 13:53:50 -0000 1.73 +++ cluster-deps.txt 19 Jan 2007 05:21:36 -0000 1.74 @@ -70,7 +70,6 @@ REQUIRES org.netbeans.modules.javahelp/1 (platform) REQUIRES org.netbeans.modules.junit/2 (ide) REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.netbeans.modules.lexer.editorbridge/1 (ide) REQUIRES org.netbeans.modules.options.api/1 (platform) REQUIRES org.netbeans.modules.project.ant/1 (ide) REQUIRES org.netbeans.modules.project.libraries/1 (ide) File [changed]: deps.txt Url: http://ide.netbeans.org/source/browse/ide/golden/deps.txt?r1=1.430&r2=1.431 Delta lines: +24 -18 --------------------- --- deps.txt 18 Jan 2007 12:06:07 -0000 1.430 +++ deps.txt 19 Jan 2007 05:21:36 -0000 1.431 @@ -482,6 +482,7 @@ REQUIRES org.openide.windows (platform) MODULE org.netbeans.modules.editor/3 (ide) REQUIRES org.netbeans.modules.editor.lib/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) REQUIRES org.netbeans.modules.editor.settings/1 (ide) REQUIRES org.openide.actions (platform) @@ -569,11 +570,22 @@ REQUIRES org.openide.util (platform) MODULE org.netbeans.modules.editor.lib/1 (ide) REQUIRES org.netbeans.modules.editor.fold/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) + REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) + REQUIRES org.netbeans.modules.editor.settings/1 (ide) REQUIRES org.netbeans.modules.editor.util/1 (ide) REQUIRES org.openide.awt (platform) REQUIRES org.openide.dialogs (platform) REQUIRES org.openide.modules.ModuleFormat1 REQUIRES org.openide.util (platform) +MODULE org.netbeans.modules.editor.lib2/1 (ide) + REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) + REQUIRES org.netbeans.modules.editor.settings/1 (ide) + REQUIRES org.netbeans.modules.editor.util/1 (ide) + REQUIRES org.netbeans.modules.lexer/2 (ide) + REQUIRES org.openide.dialogs (platform) + REQUIRES org.openide.modules.ModuleFormat1 + REQUIRES org.openide.util (platform) MODULE org.netbeans.modules.editor.mimelookup/1 (platform) REQUIRES org.openide.modules.ModuleFormat1 REQUIRES org.openide.util (platform) @@ -584,6 +596,18 @@ REQUIRES org.openide.modules.ModuleFormat1 REQUIRES org.openide.nodes (platform) REQUIRES org.openide.util (platform) +MODULE org.netbeans.modules.editor.oldlibbridge/1 (ide) + REQUIRES org.netbeans.modules.editor.lib/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) + REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) + REQUIRES org.netbeans.modules.editor.settings/1 (ide) + REQUIRES org.netbeans.modules.lexer/2 (ide) + REQUIRES org.openide.filesystems (platform) + REQUIRES org.openide.loaders (platform) + REQUIRES org.openide.modules (platform) + REQUIRES org.openide.modules.ModuleFormat1 + REQUIRES org.openide.nodes (platform) + REQUIRES org.openide.util (platform) MODULE org.netbeans.modules.editor.plain/1 (ide) REQUIRES org.netbeans.modules.editor/3 (ide) REQUIRES org.netbeans.modules.editor.lib/1 (ide) @@ -597,11 +621,7 @@ REQUIRES org.netbeans.modules.editor.lib/1 (ide) REQUIRES org.openide.modules.ModuleFormat1 MODULE org.netbeans.modules.editor.settings/1 (ide) - REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) - REQUIRES org.openide.filesystems (platform) - REQUIRES org.openide.modules (platform) REQUIRES org.openide.modules.ModuleFormat1 - REQUIRES org.openide.util (platform) MODULE org.netbeans.modules.editor.settings.storage/1 (ide) REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) REQUIRES org.netbeans.modules.editor.settings/1 (ide) @@ -694,7 +714,6 @@ REQUIRES org.netbeans.modules.html.editor.lib/1 (ide) REQUIRES org.netbeans.modules.html.lexer/1 (ide) REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.netbeans.modules.lexer.editorbridge/1 (ide) REQUIRES org.openide.awt (platform) REQUIRES org.openide.filesystems (platform) REQUIRES org.openide.modules (platform) @@ -1391,7 +1410,6 @@ REQUIRES org.netbeans.modules.java.lexer/1 (ide) REQUIRES org.netbeans.modules.java.source (ide) REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.netbeans.modules.lexer.editorbridge/1 (ide) REQUIRES org.netbeans.modules.timers/1 (ide) REQUIRES org.netbeans.spi.editor.hints/0 (ide) REQUIRES org.openide.actions (platform) @@ -1654,16 +1672,6 @@ REQUIRES org.netbeans.modules.editor.util/1 (ide) REQUIRES org.openide.modules.ModuleFormat1 REQUIRES org.openide.util (platform) -MODULE org.netbeans.modules.lexer.editorbridge/1 (ide) - REQUIRES org.netbeans.modules.editor/3 (ide) - REQUIRES org.netbeans.modules.editor.lib/1 (ide) - REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) - REQUIRES org.netbeans.modules.editor.plain.lib/1 (ide) - REQUIRES org.netbeans.modules.editor.settings/1 (ide) - REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.openide.filesystems (platform) - REQUIRES org.openide.modules.ModuleFormat1 - REQUIRES org.openide.util (platform) MODULE org.netbeans.modules.lexer.nbbridge/1 (ide) REQUIRES org.netbeans.modules.editor.mimelookup/1 (platform) REQUIRES org.netbeans.modules.lexer/2 (ide) @@ -2151,7 +2159,6 @@ REQUIRES org.netbeans.modules.java.source (ide) REQUIRES org.netbeans.modules.jsp.lexer/1 (j2ee) REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.netbeans.modules.lexer.editorbridge/1 (ide) REQUIRES org.netbeans.modules.projectapi/1 (ide) REQUIRES org.netbeans.modules.servletjspapi/1 (j2ee) REQUIRES org.netbeans.modules.web.jspparser/2 (j2ee) @@ -2593,7 +2600,6 @@ REQUIRES org.netbeans.modules.editor.lib/1 (ide) REQUIRES org.netbeans.modules.editor.structure/1 (ide) REQUIRES org.netbeans.modules.lexer/2 (ide) - REQUIRES org.netbeans.modules.lexer.editorbridge/1 (ide) REQUIRES org.netbeans.modules.xml.core/2 (ide) REQUIRES org.netbeans.modules.xml.lexer (ide) REQUIRES org.netbeans.spi.navigator/1 (ide) File [changed]: files-layout.txt Url: http://ide.netbeans.org/source/browse/ide/golden/files-layout.txt?r1=1.195&r2=1.196 Delta lines: +6 -3 ------------------- --- files-layout.txt 10 Jan 2007 20:51:51 -0000 1.195 +++ files-layout.txt 19 Jan 2007 05:21:36 -0000 1.196 @@ -637,6 +637,8 @@ ide8/config/Modules/org-netbeans-modules-editor-guards.xml ide8/config/Modules/org-netbeans-modules-editor-highlights.xml ide8/config/Modules/org-netbeans-modules-editor-lib.xml +ide8/config/Modules/org-netbeans-modules-editor-lib2.xml +ide8/config/Modules/org-netbeans-modules-editor-oldlibbridge.xml ide8/config/Modules/org-netbeans-modules-editor-plain-lib.xml ide8/config/Modules/org-netbeans-modules-editor-plain.xml ide8/config/Modules/org-netbeans-modules-editor-settings-storage.xml @@ -673,7 +675,6 @@ ide8/config/Modules/org-netbeans-modules-java-source.xml ide8/config/Modules/org-netbeans-modules-javadoc.xml ide8/config/Modules/org-netbeans-modules-junit.xml -ide8/config/Modules/org-netbeans-modules-lexer-editorbridge.xml ide8/config/Modules/org-netbeans-modules-lexer-nbbridge.xml ide8/config/Modules/org-netbeans-modules-lexer.xml ide8/config/Modules/org-netbeans-modules-options-editor.xml @@ -829,6 +830,8 @@ ide8/modules/org-netbeans-modules-editor-guards.jar ide8/modules/org-netbeans-modules-editor-highlights.jar ide8/modules/org-netbeans-modules-editor-lib.jar +ide8/modules/org-netbeans-modules-editor-lib2.jar +ide8/modules/org-netbeans-modules-editor-oldlibbridge.jar ide8/modules/org-netbeans-modules-editor-plain-lib.jar ide8/modules/org-netbeans-modules-editor-plain.jar ide8/modules/org-netbeans-modules-editor-settings-storage.jar @@ -865,7 +868,6 @@ ide8/modules/org-netbeans-modules-java-source.jar ide8/modules/org-netbeans-modules-javadoc.jar ide8/modules/org-netbeans-modules-junit.jar -ide8/modules/org-netbeans-modules-lexer-editorbridge.jar ide8/modules/org-netbeans-modules-lexer-nbbridge.jar ide8/modules/org-netbeans-modules-lexer.jar ide8/modules/org-netbeans-modules-options-editor.jar @@ -963,6 +965,8 @@ ide8/update_tracking/org-netbeans-modules-editor-guards.xml ide8/update_tracking/org-netbeans-modules-editor-highlights.xml ide8/update_tracking/org-netbeans-modules-editor-lib.xml +ide8/update_tracking/org-netbeans-modules-editor-lib2.xml +ide8/update_tracking/org-netbeans-modules-editor-oldlibbridge.xml ide8/update_tracking/org-netbeans-modules-editor-plain-lib.xml ide8/update_tracking/org-netbeans-modules-editor-plain.xml ide8/update_tracking/org-netbeans-modules-editor-settings-storage.xml @@ -999,7 +1003,6 @@ ide8/update_tracking/org-netbeans-modules-java-source.xml ide8/update_tracking/org-netbeans-modules-javadoc.xml ide8/update_tracking/org-netbeans-modules-junit.xml -ide8/update_tracking/org-netbeans-modules-lexer-editorbridge.xml ide8/update_tracking/org-netbeans-modules-lexer-nbbridge.xml ide8/update_tracking/org-netbeans-modules-lexer.xml ide8/update_tracking/org-netbeans-modules-options-editor.xml File [changed]: impl-deps.txt Url: http://ide.netbeans.org/source/browse/ide/golden/impl-deps.txt?r1=1.65&r2=1.66 Delta lines: +5 -0 ------------------- --- impl-deps.txt 21 Dec 2006 12:33:45 -0000 1.65 +++ impl-deps.txt 19 Jan 2007 05:21:36 -0000 1.66 @@ -20,10 +20,15 @@ REQUIRES org.netbeans.modules.editor.highlights/0 (ide) MODULE org.netbeans.modules.editor/3 (ide) REQUIRES org.netbeans.modules.editor.lib/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) MODULE org.netbeans.modules.editor.codetemplates/1 (ide) REQUIRES org.netbeans.spi.editor.hints/0 (ide) MODULE org.netbeans.modules.editor.errorstripe/2 (ide) REQUIRES org.netbeans.modules.editor.errorstripe.api/1 (ide) +MODULE org.netbeans.modules.editor.lib/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) +MODULE org.netbeans.modules.editor.oldlibbridge/1 (ide) + REQUIRES org.netbeans.modules.editor.lib2/1 (ide) MODULE org.netbeans.modules.j2ee.ant (j2ee) REQUIRES org.netbeans.modules.j2eeserver/4 (j2ee) MODULE org.netbeans.modules.j2ee.jboss4/1 (j2ee) File [changed]: moduleconfigs.txt Url: http://ide.netbeans.org/source/browse/ide/golden/moduleconfigs.txt?r1=1.34&r2=1.35 Delta lines: +10 -4 -------------------- --- moduleconfigs.txt 4 Jan 2007 19:20:53 -0000 1.34 +++ moduleconfigs.txt 19 Jan 2007 05:21:36 -0000 1.35 @@ -97,6 +97,8 @@ daily-alpha-nbms:editor/hints daily-alpha-nbms:editor/hints/highlights daily-alpha-nbms:editor/lib +daily-alpha-nbms:editor/lib/bridge +daily-alpha-nbms:editor/lib2 daily-alpha-nbms:editor/mimelookup daily-alpha-nbms:editor/mimelookup/impl daily-alpha-nbms:editor/options @@ -435,6 +437,8 @@ jnlp:editor/hints jnlp:editor/hints/highlights jnlp:editor/lib +jnlp:editor/lib/bridge +jnlp:editor/lib2 jnlp:editor/mimelookup jnlp:editor/mimelookup/impl jnlp:editor/options @@ -479,7 +483,6 @@ jnlp:javadoc jnlp:junit jnlp:lexer -jnlp:lexer/editorbridge jnlp:lexer/nbbridge jnlp:libs/commons_logging jnlp:libs/jsch @@ -608,6 +611,8 @@ l10nkit:editor/hints l10nkit:editor/hints/highlights l10nkit:editor/lib +l10nkit:editor/lib/bridge +l10nkit:editor/lib2 l10nkit:editor/mimelookup l10nkit:editor/mimelookup/impl l10nkit:editor/options @@ -675,7 +680,6 @@ l10nkit:javadoc l10nkit:junit l10nkit:lexer -l10nkit:lexer/editorbridge l10nkit:lexer/nbbridge l10nkit:libs/commons_logging l10nkit:libs/jsch @@ -874,6 +878,8 @@ sigtest:editor/hints sigtest:editor/hints/highlights sigtest:editor/lib +sigtest:editor/lib/bridge +sigtest:editor/lib2 sigtest:editor/mimelookup sigtest:editor/mimelookup/impl sigtest:editor/options @@ -940,7 +946,6 @@ sigtest:javadoc sigtest:junit sigtest:lexer -sigtest:lexer/editorbridge sigtest:lexer/nbbridge sigtest:libs/commons_logging sigtest:libs/jsch @@ -1097,6 +1102,8 @@ stable:editor/hints stable:editor/hints/highlights stable:editor/lib +stable:editor/lib/bridge +stable:editor/lib2 stable:editor/mimelookup stable:editor/mimelookup/impl stable:editor/options @@ -1163,7 +1170,6 @@ stable:javadoc stable:junit stable:lexer -stable:lexer/editorbridge stable:lexer/nbbridge stable:libs/commons_logging stable:libs/jsch File [changed]: modules.txt Url: http://ide.netbeans.org/source/browse/ide/golden/modules.txt?r1=1.104&r2=1.105 Delta lines: +2 -1 ------------------- --- modules.txt 16 Jan 2007 16:41:03 -0000 1.104 +++ modules.txt 19 Jan 2007 05:21:36 -0000 1.105 @@ -61,8 +61,10 @@ MODULE org.netbeans.modules.editor.guards/0 (ide) MODULE org.netbeans.modules.editor.highlights/0 (ide) MODULE org.netbeans.modules.editor.lib/1 (ide) +MODULE org.netbeans.modules.editor.lib2/1 (ide) MODULE org.netbeans.modules.editor.mimelookup/1 (platform) MODULE org.netbeans.modules.editor.mimelookup.impl/1 (platform) +MODULE org.netbeans.modules.editor.oldlibbridge/1 (ide) MODULE org.netbeans.modules.editor.plain/1 (ide) MODULE org.netbeans.modules.editor.plain.lib/1 (ide) MODULE org.netbeans.modules.editor.settings/1 (ide) @@ -130,7 +132,6 @@ MODULE org.netbeans.modules.jsp.lexer/1 (j2ee) MODULE org.netbeans.modules.junit/2 (ide) MODULE org.netbeans.modules.lexer/2 (ide) -MODULE org.netbeans.modules.lexer.editorbridge/1 (ide) MODULE org.netbeans.modules.lexer.nbbridge/1 (ide) MODULE org.netbeans.modules.masterfs/1 (platform) MODULE org.netbeans.modules.options.api/1 (platform) File [changed]: public-packages.txt Url: http://ide.netbeans.org/source/browse/ide/golden/public-packages.txt?r1=1.82&r2=1.83 Delta lines: +2 -1 ------------------- --- public-packages.txt 12 Jan 2007 13:15:56 -0000 1.82 +++ public-packages.txt 19 Jan 2007 05:21:36 -0000 1.83 @@ -187,7 +187,6 @@ org.netbeans.modules.j2ee.metadata org.netbeans.modules.j2ee.spi.ejbjar org.netbeans.modules.j2ee.spi.ejbjar.support -org.netbeans.modules.lexer.editorbridge org.netbeans.modules.properties org.netbeans.modules.refactoring.api org.netbeans.modules.refactoring.api.ui @@ -241,6 +240,8 @@ org.netbeans.spi.editor.fold org.netbeans.spi.editor.guards org.netbeans.spi.editor.guards.support +org.netbeans.spi.editor.highlighting +org.netbeans.spi.editor.highlighting.support org.netbeans.spi.editor.mimelookup org.netbeans.spi.java.classpath org.netbeans.spi.java.classpath.support Directory: /diff/src/org/netbeans/modules/diff/builtin/visualizer/ ================================================================== File [changed]: DiffPanel.java Url: http://diff.netbeans.org/source/browse/diff/src/org/netbeans/modules/diff/builtin/visualizer/DiffPanel.java?r1=1.33&r2=1.34 Delta lines: +4 -5 ------------------- --- DiffPanel.java 20 Dec 2006 06:15:50 -0000 1.33 +++ DiffPanel.java 19 Jan 2007 05:21:37 -0000 1.34 @@ -100,10 +100,9 @@ public void addNotify() { super.addNotify(); - EditorUI ui1 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane1); - ui1.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); - EditorUI ui2 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane2); - ui2.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); + + jEditorPane1.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N + jEditorPane2.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N JComponent parent = (JComponent) getParent(); File [changed]: DiffViewImpl.java Url: http://diff.netbeans.org/source/browse/diff/src/org/netbeans/modules/diff/builtin/visualizer/DiffViewImpl.java?r1=1.25&r2=1.26 Delta lines: +3 -8 ------------------- --- DiffViewImpl.java 20 Dec 2006 06:15:50 -0000 1.25 +++ DiffViewImpl.java 19 Jan 2007 05:21:37 -0000 1.26 @@ -413,14 +413,9 @@ public void addNotify() { super.addNotify(); - EditorUI ui1 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane1); - if (ui1 != null) { // null happens fpr unregistered MIME types - ui1.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); - } - EditorUI ui2 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane2); - if (ui2 != null) { // null happens fpr unregistered MIME types - ui2.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); - } + + jEditorPane1.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N + jEditorPane2.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N List actions = new ArrayList(2); actions.add(getActionMap().get("jumpNext")); // NOI18N Directory: /diff/src/org/netbeans/modules/merge/builtin/visualizer/ =================================================================== File [changed]: MergePanel.java Url: http://diff.netbeans.org/source/browse/diff/src/org/netbeans/modules/merge/builtin/visualizer/MergePanel.java?r1=1.42&r2=1.43 Delta lines: +4 -6 ------------------- --- MergePanel.java 20 Dec 2006 06:15:51 -0000 1.42 +++ MergePanel.java 19 Jan 2007 05:21:37 -0000 1.43 @@ -132,12 +132,10 @@ public void addNotify() { super.addNotify(); - EditorUI ui1 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane1); - ui1.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); - EditorUI ui2 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane2); - ui2.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); - EditorUI ui3 = org.netbeans.editor.Utilities.getEditorUI(jEditorPane3); - ui3.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); + + jEditorPane1.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N + jEditorPane2.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N + jEditorPane3.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N } Directory: /html/editor/lib/nbproject/ ====================================== File [changed]: project.xml Url: http://html.netbeans.org/source/browse/html/editor/lib/nbproject/project.xml?r1=1.13&r2=1.14 Delta lines: +0 -4 ------------------- --- project.xml 30 Nov 2006 15:45:26 -0000 1.13 +++ project.xml 19 Jan 2007 05:21:37 -0000 1.14 @@ -149,10 +149,6 @@ - org.netbeans.modules.lexer.editorbridge - - - org.netbeans.modules.lexer.nbbridge Directory: /html/editor/nbproject/ ================================== File [changed]: project.xml Url: http://html.netbeans.org/source/browse/html/editor/nbproject/project.xml?r1=1.14&r2=1.15 Delta lines: +0 -13 -------------------- --- project.xml 5 Dec 2006 09:00:21 -0000 1.14 +++ project.xml 19 Jan 2007 05:21:38 -0000 1.15 @@ -93,15 +93,6 @@ - org.netbeans.modules.lexer.editorbridge - - - - 1 - 1.3 - - - org.openide.awt @@ -192,10 +183,6 @@ org.netbeans.modules.lexer - - - - org.netbeans.modules.lexer.editorbridge Directory: /html/editor/src/org/netbeans/modules/editor/html/ ============================================================= File [changed]: HTMLKit.java Url: http://html.netbeans.org/source/browse/html/editor/src/org/netbeans/modules/editor/html/HTMLKit.java?r1=1.20&r2=1.21 Delta lines: +5 -2 ------------------- --- HTMLKit.java 30 Nov 2006 16:24:04 -0000 1.20 +++ HTMLKit.java 19 Jan 2007 05:21:38 -0000 1.21 @@ -50,11 +50,14 @@ import org.netbeans.api.lexer.Language; import org.netbeans.editor.*; +import org.netbeans.editor.BaseKit.DeleteCharAction; import org.netbeans.editor.ext.*; +import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction; import org.netbeans.editor.ext.html.*; import org.netbeans.modules.editor.NbEditorDocument; +import org.netbeans.modules.editor.NbEditorKit; +import org.netbeans.modules.editor.NbEditorKit.GenerateFoldPopupAction; import org.netbeans.modules.html.editor.folding.HTMLFoldTypes; -import org.netbeans.modules.lexer.editorbridge.LexerEditorKit; import org.openide.util.NbBundle; /** @@ -64,7 +67,7 @@ * @version 1.00 */ -public class HTMLKit extends LexerEditorKit implements org.openide.util.HelpCtx.Provider { +public class HTMLKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider { public org.openide.util.HelpCtx getHelpCtx() { return new org.openide.util.HelpCtx(HTMLKit.class); Directory: /java/editor/nbproject/ ================================== File [changed]: project.xml Url: http://java.netbeans.org/source/browse/java/editor/nbproject/project.xml?r1=1.22&r2=1.23 Delta lines: +0 -9 ------------------- --- project.xml 17 Jan 2007 10:38:50 -0000 1.22 +++ project.xml 19 Jan 2007 05:21:38 -0000 1.23 @@ -150,15 +150,6 @@ - org.netbeans.modules.lexer.editorbridge - - - - 1 - 1.3 - - - org.netbeans.modules.timers Directory: /java/editor/src/org/netbeans/modules/editor/java/ ============================================================= File [changed]: JavaKit.java Url: http://java.netbeans.org/source/browse/java/editor/src/org/netbeans/modules/editor/java/JavaKit.java?r1=1.32&r2=1.33 Delta lines: +1 -20 -------------------- --- JavaKit.java 18 Jan 2007 14:52:26 -0000 1.32 +++ JavaKit.java 19 Jan 2007 05:21:38 -0000 1.33 @@ -19,23 +19,13 @@ package org.netbeans.modules.editor.java; -import com.sun.source.tree.Tree; import java.awt.event.ActionEvent; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; import java.util.List; -import java.util.Set; -import java.util.Stack; //import javax.jmi.reflect.JmiException; import javax.swing.*; import javax.swing.text.*; import javax.swing.text.BadLocationException; -import org.netbeans.api.java.source.CompilationInfo; -import org.netbeans.api.java.source.JavaSource; -import org.netbeans.api.java.source.JavaSource.Phase; -import org.netbeans.api.java.source.SourceUtils; import org.netbeans.editor.*; import org.netbeans.editor.Utilities; import org.netbeans.editor.ext.*; @@ -46,22 +36,13 @@ import org.netbeans.editor.ext.ExtKit.CommentAction; import org.netbeans.editor.ext.ExtKit.PrefixMakerAction; import org.netbeans.editor.ext.ExtKit.UncommentAction; -import org.netbeans.lib.editor.codetemplates.api.CodeTemplate; import org.netbeans.lib.editor.codetemplates.api.CodeTemplateManager; -//import org.netbeans.jmi.javamodel.ClassDefinition; -//import org.netbeans.jmi.javamodel.Element; -//import org.netbeans.jmi.javamodel.JavaPackage; -//import org.netbeans.jmi.javamodel.Method; -//import org.netbeans.jmi.javamodel.Parameter; -import org.netbeans.modules.editor.NbEditorDocument; import org.netbeans.modules.editor.NbEditorKit; import org.netbeans.modules.editor.NbEditorUtilities; import org.netbeans.modules.java.editor.codegen.GenerateCodeAction; import org.netbeans.modules.java.editor.imports.FastImportAction; import org.netbeans.modules.java.editor.imports.JavaFixAllImports; import org.netbeans.modules.java.editor.rename.InstantRenameAction; -import org.netbeans.modules.lexer.editorbridge.LexerEditorKit; -import org.openide.ErrorManager; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.awt.Mnemonics; @@ -74,7 +55,7 @@ * @version 1.00 */ -public class JavaKit extends LexerEditorKit { +public class JavaKit extends NbEditorKit { public static final String JAVA_MIME_TYPE = "text/x-java"; // NOI18N Directory: /languages/engine/nbproject/ ======================================= File [changed]: project.xml Url: http://languages.netbeans.org/source/browse/languages/engine/nbproject/project.xml?r1=1.7&r2=1.8 Delta lines: +0 -9 ------------------- --- project.xml 18 Jan 2007 23:18:19 -0000 1.7 +++ project.xml 19 Jan 2007 05:21:39 -0000 1.8 @@ -88,15 +88,6 @@ - org.netbeans.modules.lexer.editorbridge - - - - 1 - 1.3 - - - org.netbeans.spi.navigator Directory: /languages/engine/src/org/netbeans/modules/languages/ ================================================================ File [changed]: LanguagesEditorKit.java Url: http://languages.netbeans.org/source/browse/languages/engine/src/org/netbeans/modules/languages/LanguagesEditorKit.java?r1=1.14&r2=1.15 Delta lines: +5 -3 ------------------- --- LanguagesEditorKit.java 18 Jan 2007 16:46:10 -0000 1.14 +++ LanguagesEditorKit.java 19 Jan 2007 05:21:39 -0000 1.15 @@ -22,7 +22,6 @@ import org.netbeans.api.languages.Cookie; import org.netbeans.api.languages.Cookie; import org.netbeans.api.languages.LanguagesManager; -import org.netbeans.api.languages.ParseException; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@ -59,6 +58,7 @@ import javax.swing.text.TextAction; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.editor.BaseKit.InsertBreakAction; import org.netbeans.editor.PopupManager; import org.netbeans.editor.SyntaxSupport; import org.netbeans.editor.ext.ExtSyntaxSupport; @@ -71,7 +71,9 @@ import org.netbeans.modules.languages.fold.AnnotationManager; import org.netbeans.modules.languages.fold.HyperlinkListener; import org.netbeans.api.languages.ParseException; -import org.netbeans.modules.lexer.editorbridge.LexerEditorKit; +import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction; +import org.netbeans.editor.ext.ExtKit.ExtDeleteCharAction; +import org.netbeans.modules.editor.NbEditorKit; import org.openide.ErrorManager; import org.openide.text.NbDocument; @@ -80,7 +82,7 @@ * * @author Administrator */ -public class LanguagesEditorKit extends LexerEditorKit { +public class LanguagesEditorKit extends NbEditorKit { private String mimeType; private Map documentToSyntax = new WeakHashMap (); Directory: /versioncontrol/src/org/netbeans/modules/versioning/diff/ ==================================================================== File [changed]: DiffTooltipContentPanel.java Url: http://versioncontrol.netbeans.org/source/browse/versioncontrol/src/org/netbeans/modules/versioning/diff/DiffTooltipContentPanel.java?r1=1.3&r2=1.4 Delta lines: +2 -2 ------------------- --- DiffTooltipContentPanel.java 10 Oct 2006 09:07:07 -0000 1.3 +++ DiffTooltipContentPanel.java 19 Jan 2007 05:21:39 -0000 1.4 @@ -84,8 +84,8 @@ if (maxWidth < 50) maxWidth = 50; // too thin component causes repaint problems originalTextPane.setPreferredSize(new Dimension(maxWidth * 7 / 6, height)); - if (eui != null && !originalTextPane.isEditable()) { - eui.removeLayer(ExtCaret.HIGHLIGHT_ROW_LAYER_NAME); + if (!originalTextPane.isEditable()) { + originalTextPane.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N } JScrollPane jsp = new JScrollPane(originalTextPane); Directory: /web/jspsyntax/nbproject/ ==================================== File [changed]: project.xml Url: http://web.netbeans.org/source/browse/web/jspsyntax/nbproject/project.xml?r1=1.26&r2=1.27 Delta lines: +0 -9 ------------------- --- project.xml 17 Jan 2007 16:37:20 -0000 1.26 +++ project.xml 19 Jan 2007 05:21:40 -0000 1.27 @@ -194,15 +194,6 @@ - org.netbeans.modules.lexer.editorbridge - - - - 1 - 1.3 - - - org.netbeans.modules.projectapi Directory: /web/jspsyntax/src/org/netbeans/modules/web/core/syntax/ =================================================================== File [changed]: JSPKit.java Url: http://web.netbeans.org/source/browse/web/jspsyntax/src/org/netbeans/modules/web/core/syntax/JSPKit.java?r1=1.37&r2=1.38 Delta lines: +6 -2 ------------------- --- JSPKit.java 12 Jan 2007 13:15:56 -0000 1.37 +++ JSPKit.java 19 Jan 2007 05:21:40 -0000 1.38 @@ -47,7 +47,6 @@ import org.netbeans.editor.ext.java.JavaSyntax; import org.netbeans.modules.editor.NbEditorDocument; import org.netbeans.modules.editor.NbEditorUtilities; -import org.netbeans.modules.lexer.editorbridge.LexerEditorKit; import org.netbeans.modules.web.core.syntax.folding.JspFoldTypes; import org.netbeans.spi.jsp.lexer.JspParseData; import org.netbeans.spi.lexer.TokenHierarchyControl; @@ -71,6 +70,11 @@ import org.netbeans.api.editor.fold.FoldUtilities; import org.netbeans.api.jsp.lexer.JspTokenId; import org.netbeans.api.lexer.InputAttributes; +import org.netbeans.editor.BaseKit.InsertBreakAction; +import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction; +import org.netbeans.editor.ext.ExtKit.ExtDeleteCharAction; +import org.netbeans.modules.editor.NbEditorKit; +import org.netbeans.modules.editor.NbEditorKit.GenerateFoldPopupAction; /** * Editor kit implementation for JSP content type @@ -79,7 +83,7 @@ * @author Marek.Fukala@Sun.COM * @version 1.5 */ -public class JSPKit extends LexerEditorKit implements org.openide.util.HelpCtx.Provider{ +public class JSPKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider{ public static final String JSP_MIME_TYPE = "text/x-jsp"; // NOI18N public static final String TAG_MIME_TYPE = "text/x-tag"; // NOI18N Directory: /xml/text-edit/nbproject/ ==================================== File [changed]: project.xml Url: http://xml.netbeans.org/source/browse/xml/text-edit/nbproject/project.xml?r1=1.18&r2=1.19 Delta lines: +0 -9 ------------------- --- project.xml 9 Nov 2006 16:54:15 -0000 1.18 +++ project.xml 19 Jan 2007 05:21:40 -0000 1.19 @@ -94,15 +94,6 @@ - org.netbeans.modules.lexer.editorbridge - - - - 1 - 1.3 - - - org.netbeans.modules.xml.core Directory: /xml/text-edit/src/org/netbeans/modules/xml/text/syntax/ =================================================================== File [changed]: XMLKit.java Url: http://xml.netbeans.org/source/browse/xml/text-edit/src/org/netbeans/modules/xml/text/syntax/XMLKit.java?r1=1.14&r2=1.15 Delta lines: +1 -2 ------------------- --- XMLKit.java 26 Oct 2006 20:45:29 -0000 1.14 +++ XMLKit.java 19 Jan 2007 05:21:40 -0000 1.15 @@ -31,7 +31,6 @@ import javax.swing.*; import org.netbeans.api.lexer.Language; import org.netbeans.api.xml.lexer.XMLTokenId; -import org.netbeans.modules.lexer.editorbridge.LexerEditorKit; import org.openide.awt.StatusDisplayer; // we depend on NetBeans editor stuff @@ -53,7 +52,7 @@ * @author Petr Kuzel * @author Sandeep */ -public class XMLKit extends LexerEditorKit implements org.openide.util.HelpCtx.Provider { +public class XMLKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider { /** Serial Version UID */ private static final long serialVersionUID =5326735092324267367L;