001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.editor.lib2;
043:
044: import java.lang.reflect.Method;
045: import java.util.logging.Level;
046: import java.util.logging.Logger;
047: import javax.swing.text.BadLocationException;
048: import javax.swing.text.Document;
049: import javax.swing.text.Element;
050: import org.openide.util.NbBundle;
051:
052: /**
053: * This class contains useful methods for working with documents.
054: *
055: * @author Vita Stejskal
056: */
057: public final class DocUtils {
058:
059: private static final Logger LOG = Logger.getLogger(DocUtils.class
060: .getName());
061:
062: public static int getRowStart(Document doc, int offset,
063: int lineShift) throws BadLocationException {
064:
065: checkOffsetValid(doc, offset);
066:
067: if (lineShift != 0) {
068: Element lineRoot = doc.getDefaultRootElement();
069: int line = lineRoot.getElementIndex(offset);
070: line += lineShift;
071: if (line < 0 || line >= lineRoot.getElementCount()) {
072: return -1; // invalid line shift
073: }
074: return lineRoot.getElement(line).getStartOffset();
075:
076: } else { // no shift
077: return doc.getDefaultRootElement()
078: .getElement(
079: doc.getDefaultRootElement()
080: .getElementIndex(offset))
081: .getStartOffset();
082: }
083: }
084:
085: public static int getRowEnd(Document doc, int offset)
086: throws BadLocationException {
087: checkOffsetValid(doc, offset);
088:
089: return doc.getDefaultRootElement().getElement(
090: doc.getDefaultRootElement().getElementIndex(offset))
091: .getEndOffset() - 1;
092: }
093:
094: /**
095: * Return line offset (line number - 1) for some position in the document.
096: *
097: * @param doc document to operate on
098: * @param offset position in document where to start searching
099: */
100: public static int getLineOffset(Document doc, int offset)
101: throws BadLocationException {
102: checkOffsetValid(offset, doc.getLength() + 1);
103:
104: Element lineRoot = doc.getDefaultRootElement();
105: return lineRoot.getElementIndex(offset);
106: }
107:
108: public static String debugPosition(Document doc, int offset) {
109: String ret;
110:
111: if (offset >= 0) {
112: try {
113: int line = getLineOffset(doc, offset) + 1;
114: int col = getVisualColumn(doc, offset) + 1;
115: ret = String.valueOf(line) + ":" + String.valueOf(col); // NOI18N
116: } catch (BadLocationException e) {
117: ret = NbBundle.getBundle(DocUtils.class).getString(
118: "wrong_position")
119: + ' ' + offset + " > " + doc.getLength(); // NOI18N
120: }
121: } else {
122: ret = String.valueOf(offset);
123: }
124:
125: return ret;
126: }
127:
128: /** Return visual column (with expanded tabs) on the line.
129: * @param doc document to operate on
130: * @param offset position in document for which the visual column should be found
131: * @return visual column on the line determined by position
132: */
133: public static int getVisualColumn(Document doc, int offset)
134: throws BadLocationException {
135: int docLen = doc.getLength();
136: if (offset == docLen + 1) { // at ending extra '\n' => make docLen to proceed without BLE
137: offset = docLen;
138: }
139:
140: // TODO: fix this, do not use reflection
141: try {
142: Method m = findDeclaredMethod(doc.getClass(),
143: "getVisColFromPos", Integer.TYPE); //NOI18N
144: m.setAccessible(true);
145: int col = (Integer) m.invoke(doc, offset);
146: return col;
147: // return doc.getVisColFromPos(offset);
148: } catch (Exception e) {
149: e.printStackTrace();
150: return -1;
151: }
152: }
153:
154: private static Method findDeclaredMethod(Class clazz, String name,
155: Class... parameters) throws NoSuchMethodException {
156: while (clazz != null) {
157: try {
158: return clazz.getDeclaredMethod(name, parameters);
159: } catch (NoSuchMethodException e) {
160: clazz = clazz.getSuperclass();
161: }
162: }
163: throw new NoSuchMethodException("Method: " + name); //NOI18N
164: }
165:
166: public static boolean isIdentifierPart(Document doc, char ch) {
167: // TODO: make this configurable
168: return AcceptorFactory.LETTER_DIGIT.accept(ch);
169: }
170:
171: public static boolean isWhitespace(char ch) {
172: // TODO: make this configurable
173: return AcceptorFactory.WHITESPACE.accept(ch);
174: }
175:
176: public static void atomicLock(Document doc) {
177: // TODO: fix this, do not use reflection
178: try {
179: Method lockMethod = doc.getClass().getMethod("atomicLock");
180: lockMethod.invoke(doc);
181: } catch (Exception e) {
182: LOG.log(Level.WARNING, e.getMessage(), e);
183: }
184: }
185:
186: public static void atomicUnlock(Document doc) {
187: // TODO: fix this, do not use reflection
188: try {
189: Method unlockMethod = doc.getClass().getMethod(
190: "atomicUnlock");
191: unlockMethod.invoke(doc);
192: } catch (Exception e) {
193: LOG.log(Level.WARNING, e.getMessage(), e);
194: }
195: }
196:
197: private static void checkOffsetValid(Document doc, int offset)
198: throws BadLocationException {
199: checkOffsetValid(offset, doc.getLength());
200: }
201:
202: private static void checkOffsetValid(int offset, int limitOffset)
203: throws BadLocationException {
204: if (offset < 0 || offset > limitOffset) {
205: throw new BadLocationException("Invalid offset=" + offset // NOI18N
206: + " not within <0, " + limitOffset + ">", // NOI18N
207: offset);
208: }
209: }
210:
211: /** Creates a new instance of DocUtils */
212: private DocUtils() {
213: }
214:
215: }
|