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-2007 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.search;
043:
044: import java.awt.EventQueue;
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.io.File;
048: import java.io.FileInputStream;
049: import java.io.IOException;
050: import java.io.OutputStreamWriter;
051: import java.io.Writer;
052: import java.nio.ByteBuffer;
053: import java.nio.CharBuffer;
054: import java.nio.channels.ClosedByInterruptException;
055: import java.nio.channels.FileChannel;
056: import java.nio.charset.CharacterCodingException;
057: import java.nio.charset.Charset;
058: import java.nio.charset.CharsetDecoder;
059: import java.nio.charset.CoderResult;
060: import java.nio.charset.CodingErrorAction;
061: import java.util.Arrays;
062: import java.util.List;
063: import java.util.logging.Level;
064: import java.util.logging.Logger;
065: import java.util.regex.Matcher;
066: import java.util.regex.Pattern;
067: import org.openide.filesystems.FileLock;
068: import org.openide.filesystems.FileObject;
069: import org.openide.filesystems.FileUtil;
070: import org.openide.loaders.DataObject;
071: import org.openide.util.NbBundle;
072: import static java.util.logging.Level.FINER;
073: import static java.util.logging.Level.FINEST;
074: import static java.util.logging.Level.SEVERE;
075:
076: /**
077: * Data structure holding a reference to the found object and information
078: * whether occurences in the found object should be replaced or not.
079: *
080: * @author Marian Petras
081: * @author Tim Boudreau
082: */
083: final class MatchingObject implements PropertyChangeListener {
084:
085: /** */
086: private final Logger LOG = Logger.getLogger(getClass().getName());
087: /** */
088: private final ResultModel resultModel;
089: /** */
090: private final File file;
091: /** */
092: private final long timestamp;
093:
094: /**
095: * matching object as returned by the {@code SearchGroup}
096: * (usually a {@code DataObject})
097: */
098: final Object object;
099:
100: /**
101: * holds information on whether the {@code object} is selected
102: * to be replaced or not.
103: * Unless {@link #selectedMatches} is non-{@code null}, this field's
104: * value also applies to the object's subnodes (if any).
105: *
106: * @see #selectedMatches
107: */
108: private boolean selected = true;
109: /**
110: * holds information on whether the node representing this object
111: * is expanded or collapsed
112: *
113: * @see #markExpanded(boolean)
114: */
115: private boolean expanded = false;
116: /**
117: * holds information about which matches should be replaced and which
118: * should not be replaced.
119: * Value {@code null} means that either all or none matches are selected,
120: * depending on the value of field {@link #selected}.
121: *
122: * @see #selected
123: */
124: private boolean[] selectedMatches;
125: /**
126: * flag that indicates that the tree was not notified of this
127: * {@code MatchingObject}'s children's selection change and that
128: * it must be notified before the children nodes are made visible
129: *
130: * @see #markChildrenSelectionDirty()
131: */
132: private boolean childrenSelectionDirty;
133: /** */
134: private boolean valid = true;
135: /** */
136: private StringBuilder text;
137:
138: /**
139: * {@code true} if the file's line terminator is other than {@code "\\n"}
140: */
141: boolean wasCrLf = false;
142:
143: /**
144: * Creates a new {@code MatchingObject} with a reference to the found
145: * object (returned by {@code SearchGroup}).
146: *
147: * @param object found object returned by the {@code SearchGroup}
148: * (usually a {@code DataObject}) - must not be {@code null}
149: * @exception java.lang.IllegalArgumentException
150: * if the passed {@code object} is {@code null}
151: */
152: MatchingObject(ResultModel resultModel, Object object) {
153: if (resultModel == null) {
154: throw new IllegalArgumentException("resultModel = null"); //NOI18N
155: }
156: if (object == null) {
157: throw new IllegalArgumentException("object = null"); //NOI18N
158: }
159:
160: this .resultModel = resultModel;
161: this .object = object;
162:
163: FileObject fileObject = getFileObject();
164: file = FileUtil.toFile(fileObject);
165: timestamp = (file != null) ? file.lastModified() : 0L;
166: valid = (timestamp != 0L);
167:
168: setUpDataObjValidityChecking();
169: }
170:
171: /**
172: */
173: private void setUpDataObjValidityChecking() {
174: final DataObject dataObj = (DataObject) object;
175: if (dataObj.isValid()) {
176: dataObj.addPropertyChangeListener(this );
177: }
178: }
179:
180: /**
181: */
182: void cleanup() {
183: final DataObject dataObj = (DataObject) object;
184: dataObj.removePropertyChangeListener(this );
185: }
186:
187: public void propertyChange(PropertyChangeEvent e) {
188: if (DataObject.PROP_VALID.equals(e.getPropertyName())
189: && Boolean.FALSE.equals(e.getNewValue())) {
190: assert e.getSource() == (DataObject) object;
191:
192: final DataObject dataObj = (DataObject) object;
193: dataObj.removePropertyChangeListener(this );
194:
195: resultModel.objectBecameInvalid(this );
196: }
197: }
198:
199: /**
200: * Is the {@code DataObject} encapsulated by this {@code MatchingObject}
201: * valid?
202: *
203: * @return {@code true} if the {@code DataObject} is valid, false otherwise
204: * @see DataObject#isValid
205: */
206: boolean isObjectValid() {
207: return ((DataObject) object).isValid();
208: }
209:
210: private FileObject getFileObject() {
211: return ((DataObject) object).getPrimaryFile();
212: }
213:
214: /**
215: */
216: void setSelected(boolean selected) {
217: if (selected == this .selected) {
218: return;
219: }
220:
221: this .selected = selected;
222: selectedMatches = null;
223: }
224:
225: /**
226: */
227: boolean isSelected() {
228: return selected;
229: }
230:
231: /**
232: */
233: boolean isUniformSelection() {
234: return selectedMatches == null;
235: }
236:
237: /**
238: * Checks selection of this object's subnodes.
239: *
240: * @return {@code Boolean.TRUE} if all subnodes are selected,
241: * {@code Boolean.FALSE} if all subnodes are unselected,
242: * {@code null} if some subnodes are selected and some are
243: * unselected
244: */
245: Boolean checkSubnodesSelection() {
246: if (selectedMatches == null) {
247: return Boolean.valueOf(selected);
248: }
249:
250: final boolean firstMatchSelection = selectedMatches[0];
251: for (int i = 1; i < selectedMatches.length; i++) {
252: if (selectedMatches[i] != firstMatchSelection) {
253: return null;
254: }
255: }
256: return Boolean.valueOf(firstMatchSelection);
257: }
258:
259: /**
260: */
261: void toggleSubnodeSelection(ResultModel resultModel, int index) {
262: if (selectedMatches == null) {
263: selectedMatches = new boolean[resultModel
264: .getDetailsCount(this )];
265: Arrays.fill(selectedMatches, this .selected);
266: }
267: selectedMatches[index] = !selectedMatches[index];
268: }
269:
270: /**
271: */
272: void setSubnodeSelected(int index, boolean selected,
273: ResultModel resultModel) {
274: if (selectedMatches == null) {
275: if (selected == this .selected) {
276: return;
277: }
278: selectedMatches = new boolean[resultModel
279: .getDetailsCount(this )];
280: Arrays.fill(selectedMatches, this .selected);
281: }
282:
283: assert (index >= 0) && (index < selectedMatches.length);
284: selectedMatches[index] = selected;
285: }
286:
287: /**
288: */
289: boolean isSubnodeSelected(int index) {
290: assert (selectedMatches == null)
291: || ((index >= 0) && (index < selectedMatches.length));
292: return (selectedMatches == null) ? selected
293: : selectedMatches[index];
294: }
295:
296: @Override
297: public boolean equals(Object anotherObject) {
298: return (anotherObject != null)
299: && (anotherObject.getClass() == MatchingObject.class)
300: && (((MatchingObject) anotherObject).object == this .object);
301: }
302:
303: @Override
304: public int hashCode() {
305: return object.hashCode() + 1;
306: }
307:
308: /**
309: * Sets the {@link #childrenSelectionDirty} flag.
310: *
311: * @see #markChildrenSelectionClean
312: */
313: void markChildrenSelectionDirty() {
314: childrenSelectionDirty = true;
315: }
316:
317: /**
318: * Clears the {@link #childrenSelectionDirty} flag.
319: *
320: * @see #markChildrenSelectionDirty()
321: */
322: void markChildrenSelectionClean() {
323: childrenSelectionDirty = false;
324: }
325:
326: /**
327: * Is the {@link #childrenSelectionDirty} flag set?
328: *
329: * @return {@code true} if the flag is set, {@code false} otherwise
330: * @see #markChildrenSelectionDirty()
331: */
332: boolean isChildrenSelectionDirty() {
333: return childrenSelectionDirty;
334: }
335:
336: /**
337: * Stores information whether the node representing this object is expanded
338: * or collapsed.
339: *
340: * @param expanded {@code true} if the node is expanded,
341: * {@code false} if the node is collapsed
342: * @see #isExpanded()
343: */
344: void markExpanded(boolean expanded) {
345: this .expanded = expanded;
346: }
347:
348: /**
349: * Provides information whether the node representing this object
350: * is expanded or collapsed.
351: *
352: * @return {@code true} if the node is expanded,
353: * {@code false} if the node is collapsed
354: * @see #markExpanded
355: */
356: boolean isExpanded() {
357: return expanded;
358: }
359:
360: /**
361: */
362: File getFile() {
363: return file;
364: }
365:
366: /** Get the name (not the path) of the file */
367: String getName() {
368: return getFile().getName();
369: }
370:
371: /**
372: */
373: long getTimestamp() {
374: return timestamp;
375: }
376:
377: /**
378: */
379: String getDescription() {
380: return getFile().getParent();
381: }
382:
383: /**
384: */
385: String getText() throws IOException {
386: StringBuilder txt = text();
387: if (txt != null) {
388: return txt.toString();
389: } else {
390: return null;
391: }
392: }
393:
394: /**
395: */
396: FileLock lock() throws IOException {
397: return getFileObject().lock();
398: }
399:
400: /**
401: * Reads the file if it has not been read already.
402: *
403: * @author TimBoudreau
404: * @author Marian Petras
405: */
406: private StringBuilder text() throws IOException {
407: return text(false);
408: }
409:
410: private StringBuilder text(boolean refreshCache) throws IOException {
411: assert !EventQueue.isDispatchThread();
412:
413: if (refreshCache || (text == null)) {
414: text = readText();
415: }
416: return text == null ? new StringBuilder() : text;
417: }
418:
419: /**
420: * Reads the text from the file.
421: *
422: * @return {@code StringBuilder} containing the text file's content,
423: * or {@code null} if reading was interrupted
424: * @exception java.io.IOException if some error occured while reading
425: * the file
426: */
427: private StringBuilder readText() throws IOException {
428: StringBuilder ret = null;
429:
430: ByteBuffer buf = getByteBuffer();
431: if (buf != null) {
432: Charset charset = BasicSearchCriteria
433: .getCharset(getFileObject());
434: CharBuffer cbuf = decodeByteBuffer(buf, charset);
435: String terminator = System.getProperty("line.separator"); //NOI18N
436:
437: if (!terminator.equals("\n")) { //NOI18N
438: Matcher matcher = Pattern.compile(terminator).matcher(
439: cbuf);
440: if (matcher.find()) {
441: wasCrLf = true;
442: matcher.reset();
443: ret = new StringBuilder(matcher.replaceAll("\n")); //NOI18N
444: }
445: }
446: if (ret == null) {
447: ret = new StringBuilder(cbuf);
448: }
449: }
450: return ret;
451: }
452:
453: /**
454: *
455: * @author Tim Boudreau
456: */
457: private ByteBuffer getByteBuffer() throws IOException {
458: assert !EventQueue.isDispatchThread();
459:
460: File file = getFile();
461: //XXX optimize with a single shared bytebuffer if performance
462: //problems noted
463: FileInputStream str = new FileInputStream(file);
464:
465: ByteBuffer buffer = ByteBuffer.allocate((int) file.length());
466: FileChannel channel = str.getChannel();
467: try {
468: channel.read(buffer, 0);
469: } catch (ClosedByInterruptException cbie) {
470: return null; //this is actually okay
471: } finally {
472: channel.close();
473: }
474: buffer.rewind();
475: return buffer;
476: }
477:
478: /**
479: * Describes invalidity status of this item.
480: */
481: enum InvalidityStatus {
482:
483: DELETED(true, "Inv_status_Err_deleted"), //NOI18N
484: BECAME_DIR(true, "Inv_status_Err_became_dir"), //NOI18N
485: CHANGED(false, "Inv_status_Err_changed"), //NOI18N
486: TOO_BIG(false, "Inv_status_Err_too_big"), //NOI18N
487: CANT_READ(false, "Inv_status_Err_cannot_read"); //NOI18N
488:
489: /**
490: * Is this invalidity the fatal one?
491: *
492: * @see #isFatal()
493: */
494: private final boolean fatal;
495: /**
496: * resource bundle key for the description
497: *
498: * @see #getDescription(String)
499: */
500: private final String descrBundleKey;
501:
502: /**
503: * Creates an invalidity status.
504: *
505: * @param fatal whether this status means that the invalidity is fatal
506: * @see #isFatal()
507: */
508: private InvalidityStatus(boolean fatal, String descrBundleKey) {
509: this .fatal = fatal;
510: this .descrBundleKey = descrBundleKey;
511: }
512:
513: /**
514: * Is this invalidity fatal such that the item should be removed
515: * from the search results?
516: */
517: boolean isFatal() {
518: return fatal;
519: }
520:
521: /**
522: * Provides human-readable description of this invalidity status.
523: *
524: * @param path path or name of file that has this invalidity status
525: * @return description of the invalidity status with the given path
526: * or name embedded
527: */
528: String getDescription(String path) {
529: return NbBundle
530: .getMessage(getClass(), descrBundleKey, path);
531: }
532:
533: }
534:
535: /**
536: */
537: InvalidityStatus checkValidity() {
538: InvalidityStatus status = getInvalidityStatus();
539: if (status != null) {
540: valid = false;
541: }
542: return status;
543: }
544:
545: /**
546: */
547: String getInvalidityDescription() {
548: String descr;
549:
550: InvalidityStatus status = getInvalidityStatus();
551: if (status != null) {
552: descr = status.getDescription(getFile().getPath());
553: } else {
554: descr = null;
555: }
556: return descr;
557: }
558:
559: /**
560: * Check validity status of this item.
561: *
562: * @return an invalidity status of this item if it is invalid,
563: * or {@code null} if this item is valid
564: * @author Tim Boudreau
565: * @author Marian Petras
566: */
567: private InvalidityStatus getInvalidityStatus() {
568: log(FINER, "getInvalidityStatus()"); //NOI18N
569: File f = getFile();
570: if (!f.exists()) {
571: log(FINEST, " - DELETED");
572: return InvalidityStatus.DELETED;
573: }
574:
575: if (f.isDirectory()) {
576: log(FINEST, " - BECAME_DIR");
577: return InvalidityStatus.BECAME_DIR;
578: }
579:
580: long stamp = f.lastModified();
581: if (stamp > resultModel.getCreationTime()) {
582: log(SEVERE,
583: "file's timestamp changed since start of the search");
584: if (LOG.isLoggable(FINEST)) {
585: final java.util.Calendar cal = java.util.Calendar
586: .getInstance();
587: cal.setTimeInMillis(stamp);
588: log(FINEST, " - file stamp: " + stamp + " ("
589: + cal.getTime() + ')');
590: cal.setTimeInMillis(resultModel.getCreationTime());
591: log(FINEST, " - result model created: "
592: + resultModel.getCreationTime() + " ("
593: + cal.getTime() + ')');
594: }
595: return InvalidityStatus.CHANGED;
596: }
597:
598: if (f.length() > Integer.MAX_VALUE) {
599: return InvalidityStatus.TOO_BIG;
600: }
601:
602: if (!f.canRead()) {
603: return InvalidityStatus.CANT_READ;
604: }
605:
606: return null;
607: }
608:
609: /**
610: */
611: boolean isValid() {
612: return valid;
613: }
614:
615: /**
616: */
617: public InvalidityStatus replace() throws IOException {
618: assert !EventQueue.isDispatchThread();
619: assert isSelected();
620:
621: Boolean uniformSelection = checkSubnodesSelection();
622: final boolean shouldReplaceAll = (uniformSelection == Boolean.TRUE);
623: final boolean shouldReplaceNone = (uniformSelection == Boolean.FALSE);
624:
625: if (shouldReplaceNone) {
626: return null;
627: }
628:
629: StringBuilder content = text(true); //refresh the cache, reads the file
630:
631: List<TextDetail> textMatches = resultModel.basicCriteria
632: .getTextDetails(object);
633: int matchIndex = 0;
634:
635: int currLineOffset = 0;
636: int currLine = 1;
637:
638: int inlineMatchNumber = 0; //order of a match in a line
639: int inlineOffsetShift = 0; //shift of offsets caused by replacements
640:
641: mainloop: for (TextDetail textDetail : textMatches) {
642: int matchLine = textDetail.getLine();
643:
644: while (currLine < matchLine) {
645: int lfOffset = content.indexOf("\n", currLineOffset);//NOI18N
646: if (lfOffset == -1) {
647: assert false; //PENDING - should notify user
648: break mainloop;
649: }
650:
651: currLineOffset = lfOffset + 1; //skips "\n"
652: currLine++;
653: inlineMatchNumber = 0;
654: inlineOffsetShift = 0;
655: }
656:
657: if (!isSubnodeSelected(matchIndex++)) {
658: continue;
659: }
660:
661: if (++inlineMatchNumber == 1) { //first selected match on a line
662: boolean check = false;
663: assert check = true; //side-effect - turns the check on
664: if (check) {
665: int lineEndOffset = content.indexOf("\n", //NOI18N
666: currLineOffset);
667: String fileLine = (lineEndOffset != -1) ? content
668: .substring(currLineOffset, lineEndOffset)
669: : content.substring(currLineOffset);
670: if (!fileLine.equals(textDetail.getLineText())) {
671: log(SEVERE,
672: "file line differs from the expected line");
673: if (LOG.isLoggable(FINEST)) {
674: log(SEVERE, " - expected line: \""
675: + textDetail.getLineText() + '"');
676: log(SEVERE, " - file line: \""
677: + fileLine + '"');
678: }
679: return InvalidityStatus.CHANGED;
680: }
681: }
682: }
683:
684: int matchLength = textDetail.getMarkLength();
685: int matchOffset = currLineOffset + inlineOffsetShift
686: + (textDetail.getColumn() - 1);
687: int matchEndOffset = matchOffset + matchLength;
688: if (!content.substring(matchOffset, matchEndOffset).equals(
689: textDetail.getLineText().substring(
690: textDetail.getColumn() - 1,
691: textDetail.getColumn() - 1 + matchLength))) {
692: log(SEVERE,
693: "file match part differs from the expected match");
694: if (LOG.isLoggable(FINEST)) {
695: log(SEVERE, " - expected line: \""
696: + textDetail.getLineText().substring(
697: textDetail.getColumn() - 1,
698: textDetail.getColumn() - 1
699: + matchLength) + '"');
700: log(SEVERE, " - file line: \""
701: + content.substring(matchOffset,
702: matchEndOffset) + '"');
703: }
704: return InvalidityStatus.CHANGED;
705: }
706:
707: content.replace(matchOffset, matchEndOffset,
708: resultModel.replaceString);
709: inlineOffsetShift += resultModel.replaceString.length()
710: - matchLength;
711: }
712: return null;
713: }
714:
715: /** debug flag */
716: private static final boolean REALLY_WRITE = true;
717:
718: /**
719: */
720: void write(final FileLock fileLock) throws IOException {
721: if (text == null) {
722: throw new IllegalStateException("Buffer is gone"); //NOI18N
723: }
724:
725: if (REALLY_WRITE) {
726: if (wasCrLf) {
727: String terminator = System
728: .getProperty("line.separator"); //NOI18N
729: //XXX use constant - i.e. on mac, only \r, etc.
730: text = new StringBuilder(text.toString().replace("\n",
731: terminator)); //NOI18N
732: }
733: final FileObject fileObject = getFileObject();
734: Writer writer = null;
735: try {
736: writer = new OutputStreamWriter(fileObject
737: .getOutputStream(fileLock), BasicSearchCriteria
738: .getCharset(fileObject));
739: writer.write(text.toString());
740: } finally {
741: if (writer != null) {
742: writer.close();
743: }
744: }
745: } else {
746: System.err.println("Would write to " + getFile().getPath());//NOI18N
747: System.err.println(text);
748: }
749: }
750:
751: /**
752: * Decodes a given {@code ByteBuffer} with a given charset decoder.
753: * This is a workaround for a broken
754: * {@link Charset.decode(ByteBuffer) Charset#decode(java.nio.ByteBuffer}
755: * method in JDK 1.5.x.
756: *
757: * @param in {@code ByteBuffer} to be decoded
758: * @param charset charset whose decoder will be used for decoding
759: * @return {@code CharBuffer} containing chars produced by the decoder
760: * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/nio/charset/Charset.html#decode(java.nio.ByteBuffer)">Charset.decode(ByteBuffer)</a>
761: * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6221056">JDK bug #6221056</a>
762: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=103193">NetBeans bug #103193</a>
763: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=103067">NetBeans bug #103067</a>
764: */
765: private CharBuffer decodeByteBuffer(final ByteBuffer in,
766: final Charset charset) throws CharacterCodingException {
767:
768: final CharsetDecoder decoder = charset.newDecoder()
769: .onMalformedInput(CodingErrorAction.REPLACE)
770: .onUnmappableCharacter(CodingErrorAction.REPLACE);
771:
772: int remaining = in.remaining();
773: if (remaining == 0) {
774: return CharBuffer.allocate(0);
775: }
776:
777: int n = (int) (remaining * decoder.averageCharsPerByte());
778: if (n < 16) {
779: n = 16; //make sure some CharBuffer is allocated
780: //even when decoding small number of bytes
781: //and averageCharsPerByte() is less than 1
782: }
783: CharBuffer out = CharBuffer.allocate(n);
784:
785: decoder.reset();
786: for (;;) {
787: CoderResult cr = in.hasRemaining() ? decoder.decode(in,
788: out, true) : CoderResult.UNDERFLOW;
789: if (cr.isUnderflow()) {
790: cr = decoder.flush(out);
791: }
792: if (cr.isUnderflow()) {
793: break;
794: }
795: if (cr.isOverflow()) {
796: CharBuffer o = CharBuffer.allocate(n <<= 1);
797: out.flip();
798: o.put(out);
799: out = o;
800: continue;
801: }
802: cr.throwException();
803: }
804: out.flip();
805: return out;
806: }
807:
808: /**
809: *
810: */
811: private void log(Level logLevel, String msg) {
812: String id = (object instanceof DataObject) ? ((DataObject) object)
813: .getName()
814: : object.toString();
815: if (LOG.isLoggable(logLevel)) {
816: LOG.log(logLevel, id + ": " + msg); //NO1I8N:w
817: }
818: }
819:
820: /** Returns name of this node.
821: * @return name of this node.
822: */
823: @Override
824: public String toString() {
825: return super .toString() + "[" + getName() + "]"; // NOI18N
826: }
827: }
|