Source Code Cross Referenced for RubyParser.java in  » IDE-Netbeans » ruby » org » netbeans » modules » ruby » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » ruby » org.netbeans.modules.ruby 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        package org.netbeans.modules.ruby;
042:
043:        import org.netbeans.modules.gsf.api.ParserResult.AstTreeNode;
044:        import java.io.IOException;
045:        import java.io.Reader;
046:        import java.io.StringReader;
047:        import java.util.Iterator;
048:        import java.util.List;
049:        import javax.swing.text.BadLocationException;
050:        import org.jruby.ast.Node;
051:        import org.jruby.ast.RootNode;
052:        import org.jruby.common.IRubyWarnings;
053:        import org.jruby.lexer.yacc.ISourcePosition;
054:        import org.jruby.lexer.yacc.LexerSource;
055:        import org.jruby.lexer.yacc.SyntaxException;
056:        import org.jruby.parser.DefaultRubyParser;
057:        import org.jruby.parser.RubyParserConfiguration;
058:        import org.jruby.parser.RubyParserResult;
059:        import org.netbeans.modules.gsf.api.CompilationInfo;
060:        import org.netbeans.modules.ruby.elements.Element;
061:        import org.netbeans.modules.gsf.api.ElementHandle;
062:        import org.netbeans.modules.gsf.api.Error;
063:        import org.netbeans.modules.gsf.api.OffsetRange;
064:        import org.netbeans.modules.gsf.api.ParseEvent;
065:        import org.netbeans.modules.gsf.api.ParseListener;
066:        import org.netbeans.modules.gsf.api.Parser;
067:        import org.netbeans.modules.gsf.api.ParserFile;
068:        import org.netbeans.modules.gsf.api.ParserResult;
069:        import org.netbeans.modules.gsf.api.PositionManager;
070:        import org.netbeans.modules.gsf.api.Severity;
071:        import org.netbeans.modules.gsf.api.SourceFileReader;
072:        import org.netbeans.modules.gsf.api.TranslatedSource;
073:        import org.netbeans.modules.ruby.elements.AstElement;
074:        import org.netbeans.modules.ruby.elements.RubyElement;
075:        import org.netbeans.modules.gsf.spi.DefaultError;
076:        import org.openide.util.Exceptions;
077:        import org.openide.util.NbBundle;
078:
079:        /**
080:         * Wrapper around JRuby to parse a buffer into an AST.
081:         *
082:         * @todo Rename to RubyParser for symmetry with RubyLexer
083:         * @todo Idea: If you get a syntax error on the last line, it's probably a missing
084:         *   "end" much earlier. Go back and look for a method inside a method, and the outer
085:         *   method is probably missing an end (can use indentation to look for this as well).
086:         *   Create a quickfix to insert it.
087:         * @todo Only look for missing-end if there's an unexpected end
088:         * @todo If you get a "class definition in method body" error, there's a missing
089:         *   end - prior to the class!
090:         * @todo "syntax error, unexpected tRCURLY" means that I also have a missing end,
091:         *   but we encountered a } before we got to it. I need to be bracketing this stuff.
092:         * 
093:         * @author Tor Norbye
094:         */
095:        public final class RubyParser implements  Parser {
096:            private final PositionManager positions = createPositionManager();
097:
098:            /**
099:             * Creates a new instance of RubyParser
100:             */
101:            public RubyParser() {
102:            }
103:
104:            private static String asString(CharSequence sequence) {
105:                if (sequence instanceof  String) {
106:                    return (String) sequence;
107:                } else {
108:                    return sequence.toString();
109:                }
110:            }
111:
112:            /** Parse the given set of files, and notify the parse listener for each transition
113:             * (compilation results are attached to the events )
114:             */
115:            public void parseFiles(Parser.Job job) {
116:                ParseListener listener = job.listener;
117:                SourceFileReader reader = job.reader;
118:
119:                for (ParserFile file : job.files) {
120:                    ParseEvent beginEvent = new ParseEvent(
121:                            ParseEvent.Kind.PARSE, file, null);
122:                    listener.started(beginEvent);
123:
124:                    ParserResult result = null;
125:
126:                    try {
127:                        CharSequence buffer = reader.read(file);
128:                        String source = asString(buffer);
129:                        int caretOffset = reader.getCaretOffset(file);
130:                        if (caretOffset != -1 && job.translatedSource != null) {
131:                            caretOffset = job.translatedSource
132:                                    .getAstOffset(caretOffset);
133:                        }
134:                        Context context = new Context(file, listener, source,
135:                                caretOffset, job.translatedSource);
136:                        result = parseBuffer(context, Sanitize.NONE);
137:                    } catch (IOException ioe) {
138:                        listener.exception(ioe);
139:                    }
140:
141:                    ParseEvent doneEvent = new ParseEvent(
142:                            ParseEvent.Kind.PARSE, file, result);
143:                    listener.finished(doneEvent);
144:                }
145:            }
146:
147:            protected PositionManager createPositionManager() {
148:                return new RubyPositionManager();
149:            }
150:
151:            /**
152:             * Try cleaning up the source buffer around the current offset to increase
153:             * likelihood of parse success. Initially this method had a lot of
154:             * logic to determine whether a parse was likely to fail (e.g. invoking
155:             * the isEndMissing method from bracket completion etc.).
156:             * However, I am now trying a parse with the real source first, and then
157:             * only if that fails do I try parsing with sanitized source. Therefore,
158:             * this method has to be less conservative in ripping out code since it
159:             * will only be used when the regular source is failing.
160:             */
161:            private boolean sanitizeSource(Context context, Sanitize sanitizing) {
162:
163:                if (sanitizing == Sanitize.MISSING_END) {
164:                    context.sanitizedSource = context.source + ";end";
165:                    int start = context.source.length();
166:                    context.sanitizedRange = new OffsetRange(start, start + 4);
167:                    context.sanitizedContents = "";
168:                    return true;
169:                }
170:
171:                int offset = context.caretOffset;
172:
173:                // Let caretOffset represent the offset of the portion of the buffer we'll be operating on
174:                if ((sanitizing == Sanitize.ERROR_DOT)
175:                        || (sanitizing == Sanitize.ERROR_LINE)) {
176:                    offset = context.errorOffset;
177:                }
178:
179:                // Don't attempt cleaning up the source if we don't have the buffer position we need
180:                if (offset == -1) {
181:                    return false;
182:                }
183:
184:                // The user might be editing around the given caretOffset.
185:                // See if it looks modified
186:                // Insert an end statement? Insert a } marker?
187:                String doc = context.source;
188:                if (offset > doc.length()) {
189:                    return false;
190:                }
191:
192:                if (sanitizing == Sanitize.BLOCK_START) {
193:                    try {
194:                        int start = RubyUtils.getRowFirstNonWhite(doc, offset);
195:                        if (start != -1 && start + 2 < doc.length()
196:                                && doc.regionMatches(start, "if", 0, 2)) {
197:                            // TODO - check lexer
198:                            char c = 0;
199:                            if (start + 2 < doc.length()) {
200:                                c = doc.charAt(start + 2);
201:                            }
202:                            if (!Character.isLetter(c)) {
203:                                int removeStart = start;
204:                                int removeEnd = removeStart + 2;
205:                                StringBuilder sb = new StringBuilder(doc
206:                                        .length());
207:                                sb.append(doc.substring(0, removeStart));
208:                                for (int i = removeStart; i < removeEnd; i++) {
209:                                    sb.append(' ');
210:                                }
211:                                if (removeEnd < doc.length()) {
212:                                    sb.append(doc.substring(removeEnd, doc
213:                                            .length()));
214:                                }
215:                                assert sb.length() == doc.length();
216:                                context.sanitizedRange = new OffsetRange(
217:                                        removeStart, removeEnd);
218:                                context.sanitizedSource = sb.toString();
219:                                context.sanitizedContents = doc.substring(
220:                                        removeStart, removeEnd);
221:                                return true;
222:                            }
223:                        }
224:
225:                        return false;
226:                    } catch (BadLocationException ble) {
227:                        Exceptions.printStackTrace(ble);
228:                        return false;
229:                    }
230:                }
231:
232:                try {
233:                    // Sometimes the offset shows up on the next line
234:                    if (RubyUtils.isRowEmpty(doc, offset)
235:                            || RubyUtils.isRowWhite(doc, offset)) {
236:                        offset = RubyUtils.getRowStart(doc, offset) - 1;
237:                        if (offset < 0) {
238:                            offset = 0;
239:                        }
240:                    }
241:
242:                    if (!(RubyUtils.isRowEmpty(doc, offset) || RubyUtils
243:                            .isRowWhite(doc, offset))) {
244:                        if ((sanitizing == Sanitize.EDITED_LINE)
245:                                || (sanitizing == Sanitize.ERROR_LINE)) {
246:                            // See if I should try to remove the current line, since it has text on it.
247:                            int lineEnd = RubyUtils.getRowLastNonWhite(doc,
248:                                    offset);
249:
250:                            if (lineEnd != -1) {
251:                                StringBuilder sb = new StringBuilder(doc
252:                                        .length());
253:                                int lineStart = RubyUtils.getRowStart(doc,
254:                                        offset);
255:                                int rest = lineStart + 1;
256:
257:                                sb.append(doc.substring(0, lineStart));
258:                                sb.append('#');
259:
260:                                if (rest < doc.length()) {
261:                                    sb
262:                                            .append(doc.substring(rest, doc
263:                                                    .length()));
264:                                }
265:                                assert sb.length() == doc.length();
266:
267:                                context.sanitizedRange = new OffsetRange(
268:                                        lineStart, lineEnd);
269:                                context.sanitizedSource = sb.toString();
270:                                context.sanitizedContents = doc.substring(
271:                                        lineStart, lineEnd);
272:                                return true;
273:                            }
274:                        } else {
275:                            assert sanitizing == Sanitize.ERROR_DOT
276:                                    || sanitizing == Sanitize.EDITED_DOT;
277:                            // Try nuking dots/colons from this line
278:                            // See if I should try to remove the current line, since it has text on it.
279:                            int lineStart = RubyUtils.getRowStart(doc, offset);
280:                            int lineEnd = offset - 1;
281:                            while (lineEnd >= lineStart
282:                                    && lineEnd < doc.length()) {
283:                                if (!Character
284:                                        .isWhitespace(doc.charAt(lineEnd))) {
285:                                    break;
286:                                }
287:                                lineEnd--;
288:                            }
289:                            if (lineEnd > lineStart) {
290:                                StringBuilder sb = new StringBuilder(doc
291:                                        .length());
292:                                String line = doc.substring(lineStart,
293:                                        lineEnd + 1);
294:                                int removeChars = 0;
295:                                int removeEnd = lineEnd + 1;
296:
297:                                if (line.endsWith(".") || line.endsWith("(")) { // NOI18N
298:                                    removeChars = 1;
299:                                } else if (line.endsWith(",")) { // NOI18N                            removeChars = 1;
300:                                    removeChars = 1;
301:                                } else if (line.endsWith(",:")) { // NOI18N
302:                                    removeChars = 2;
303:                                } else if (line.endsWith(", :")) { // NOI18N
304:                                    removeChars = 3;
305:                                } else if (line.endsWith(", ")) { // NOI18N
306:                                    removeChars = 2;
307:                                } else if (line.endsWith("=> :")) { // NOI18N
308:                                    removeChars = 4;
309:                                } else if (line.endsWith("=>:")) { // NOI18N
310:                                    removeChars = 3;
311:                                } else if (line.endsWith("=>")) { // NOI18N
312:                                    removeChars = 2;
313:                                } else if (line.endsWith("::")) { // NOI18N
314:                                    removeChars = 2;
315:                                } else if (line.endsWith(":")) { // NOI18N
316:                                    removeChars = 1;
317:                                } else if (line.endsWith("@@")) { // NOI18N
318:                                    removeChars = 2;
319:                                } else if (line.endsWith("@")) { // NOI18N
320:                                    removeChars = 1;
321:                                } else if (line.endsWith(",)")) { // NOI18N
322:                                    // Handle lone comma in parameter list - e.g.
323:                                    // type "foo(a," -> you end up with "foo(a,|)" which doesn't parse - but
324:                                    // the line ends with ")", not "," !
325:                                    // Just remove the comma
326:                                    removeChars = 1;
327:                                    removeEnd--;
328:                                } else if (line.endsWith(", )")) { // NOI18N
329:                                    // Just remove the comma
330:                                    removeChars = 1;
331:                                    removeEnd -= 2;
332:                                }
333:
334:                                if (removeChars == 0) {
335:                                    return false;
336:                                }
337:
338:                                int removeStart = removeEnd - removeChars;
339:
340:                                sb.append(doc.substring(0, removeStart));
341:
342:                                for (int i = 0; i < removeChars; i++) {
343:                                    sb.append(' ');
344:                                }
345:
346:                                if (removeEnd < doc.length()) {
347:                                    sb.append(doc.substring(removeEnd, doc
348:                                            .length()));
349:                                }
350:                                assert sb.length() == doc.length();
351:
352:                                context.sanitizedRange = new OffsetRange(
353:                                        removeStart, removeEnd);
354:                                context.sanitizedSource = sb.toString();
355:                                context.sanitizedContents = doc.substring(
356:                                        removeStart, removeEnd);
357:                                return true;
358:                            }
359:                        }
360:                    }
361:                } catch (BadLocationException ble) {
362:                    Exceptions.printStackTrace(ble);
363:                }
364:
365:                return false;
366:            }
367:
368:            @SuppressWarnings("fallthrough")
369:            private RubyParseResult sanitize(final Context context,
370:                    final Sanitize sanitizing) {
371:
372:                switch (sanitizing) {
373:                case NEVER:
374:                    return createParseResult(context.file, null, null, null,
375:                            null);
376:
377:                case NONE:
378:
379:                    // We've currently tried with no sanitization: try first level
380:                    // of sanitization - removing dots/colons at the edited offset.
381:                    // First try removing the dots or double colons around the failing position
382:                    if (context.caretOffset != -1) {
383:                        return parseBuffer(context, Sanitize.EDITED_DOT);
384:                    }
385:
386:                    // Fall through to try the next trick
387:                case EDITED_DOT:
388:
389:                    // We've tried editing the caret location - now try editing the error location
390:                    // (Don't bother doing this if errorOffset==caretOffset since that would try the same
391:                    // source as EDITED_DOT which has no better chance of succeeding...)
392:                    if (context.errorOffset != -1
393:                            && context.errorOffset != context.caretOffset) {
394:                        return parseBuffer(context, Sanitize.ERROR_DOT);
395:                    }
396:
397:                    // Fall through to try the next trick
398:                case ERROR_DOT:
399:
400:                    // We've tried removing dots - now try removing the whole line at the error position
401:                    if (context.caretOffset != -1) {
402:                        return parseBuffer(context, Sanitize.BLOCK_START);
403:                    }
404:
405:                    // Fall through to try the next trick
406:                case BLOCK_START:
407:
408:                    // We've tried removing dots - now try removing the whole line at the error position
409:                    if (context.errorOffset != -1) {
410:                        return parseBuffer(context, Sanitize.ERROR_LINE);
411:                    }
412:
413:                    // Fall through to try the next trick
414:                case ERROR_LINE:
415:
416:                    // Messing with the error line didn't work - we could try "around" the error line
417:                    // but I'm not attempting that now.
418:                    // Finally try removing the whole line around the user editing position
419:                    // (which could be far from where the error is showing up - but if you're typing
420:                    // say a new "def" statement in a class, this will show up as an error on a mismatched
421:                    // "end" statement rather than here
422:                    if (context.caretOffset != -1) {
423:                        return parseBuffer(context, Sanitize.EDITED_LINE);
424:                    }
425:
426:                    // Fall through to try the next trick
427:                case EDITED_LINE:
428:                    return parseBuffer(context, Sanitize.MISSING_END);
429:
430:                    // Fall through for default handling
431:                case MISSING_END:
432:                default:
433:                    // We're out of tricks - just return the failed parse result
434:                    return createParseResult(context.file, null, null, null,
435:                            null);
436:                }
437:            }
438:
439:            protected void notifyError(Context context, String key,
440:                    Severity severity, String description, String details,
441:                    int offset, Sanitize sanitizing) {
442:                // Replace a common but unwieldy JRuby error message with a shorter one
443:                if (description.startsWith("syntax error, expecting	")) { // NOI18N
444:                    int start = description.indexOf(" but found "); // NOI18N
445:                    assert start != -1;
446:                    start += 11;
447:                    int end = description.indexOf("instead", start); // NOI18N
448:                    assert end != -1;
449:                    String found = description.substring(start, end);
450:                    description = details = NbBundle.getMessage(
451:                            RubyParser.class, "UnexpectedError", found);
452:                }
453:
454:                // Initialize keys for errors needing it
455:                if (key == null) {
456:                    key = description;
457:                }
458:
459:                Error error = new DefaultError(key, description, details,
460:                        context.file.getFileObject(), offset, offset, severity);
461:                context.listener.error(error);
462:
463:                if (sanitizing == Sanitize.NONE) {
464:                    context.errorOffset = offset;
465:                }
466:            }
467:
468:            protected RubyParseResult parseBuffer(final Context context,
469:                    final Sanitize sanitizing) {
470:                boolean sanitizedSource = false;
471:                String source = context.source;
472:                if (!((sanitizing == Sanitize.NONE) || (sanitizing == Sanitize.NEVER))) {
473:                    boolean ok = sanitizeSource(context, sanitizing);
474:
475:                    if (ok) {
476:                        assert context.sanitizedSource != null;
477:                        sanitizedSource = true;
478:                        source = context.sanitizedSource;
479:                    } else {
480:                        // Try next trick
481:                        return sanitize(context, sanitizing);
482:                    }
483:                }
484:
485:                Reader content = new StringReader(source);
486:
487:                RubyParserResult result = null;
488:
489:                final boolean ignoreErrors = sanitizedSource;
490:
491:                try {
492:                    IRubyWarnings warnings = new IRubyWarnings() {
493:                        public void warn(ISourcePosition position,
494:                                String message) {
495:                            if (!ignoreErrors) {
496:                                notifyError(context, null, Severity.WARNING,
497:                                        message, null, position
498:                                                .getStartOffset(), sanitizing);
499:                            }
500:                        }
501:
502:                        public boolean isVerbose() {
503:                            return false;
504:                        }
505:
506:                        public void warn(String message) {
507:                            if (!ignoreErrors) {
508:                                notifyError(context, null, Severity.WARNING,
509:                                        message, null, -1, sanitizing);
510:                            }
511:                        }
512:
513:                        public void warning(String message) {
514:                            if (!ignoreErrors) {
515:                                notifyError(context, null, Severity.WARNING,
516:                                        message, null, -1, sanitizing);
517:                            }
518:                        }
519:
520:                        public void warning(ISourcePosition position,
521:                                String message) {
522:                            if (!ignoreErrors) {
523:                                notifyError(context, null, Severity.WARNING,
524:                                        message, null, position
525:                                                .getStartOffset(), sanitizing);
526:                            }
527:                        }
528:                    };
529:
530:                    //warnings.setFile(file);
531:                    DefaultRubyParser parser = new DefaultRubyParser();
532:                    parser.setWarnings(warnings);
533:
534:                    if (sanitizing == Sanitize.NONE) {
535:                        context.errorOffset = -1;
536:                    }
537:
538:                    String fileName = "";
539:
540:                    if ((context.file != null)
541:                            && (context.file.getFileObject() != null)) {
542:                        fileName = context.file.getFileObject().getNameExt();
543:                    }
544:
545:                    LexerSource lexerSource = new LexerSource(fileName,
546:                            content, 0, true);
547:                    RubyParserConfiguration configuration = new RubyParserConfiguration();
548:                    result = parser.parse(configuration, lexerSource);
549:                } catch (SyntaxException e) {
550:                    int offset = e.getPosition().getStartOffset();
551:
552:                    // XXX should this be >, and = length?
553:                    if (offset >= source.length()) {
554:                        offset = source.length() - 1;
555:
556:                        if (offset < 0) {
557:                            offset = 0;
558:                        }
559:                    }
560:
561:                    if (!ignoreErrors) {
562:                        notifyError(context, null, Severity.ERROR, e
563:                                .getMessage(), e.getLocalizedMessage(), offset,
564:                                sanitizing);
565:                    }
566:                }
567:
568:                Node root = (result != null) ? result.getAST() : null;
569:
570:                RootNode realRoot = null;
571:
572:                if (root instanceof  RootNode) {
573:                    // Quick workaround for now to avoid NPEs all over when
574:                    // code looks at RootNode, whose getPosition()==null.
575:                    // Its bodynode is what used to be returned as the root!
576:                    realRoot = (RootNode) root;
577:                    root = realRoot.getBodyNode();
578:                }
579:
580:                if (root != null) {
581:                    context.sanitized = sanitizing;
582:                    AstNodeAdapter ast = new AstNodeAdapter(null, root);
583:                    RubyParseResult r = createParseResult(context.file, ast,
584:                            root, realRoot, result);
585:                    r.setSanitized(context.sanitized, context.sanitizedRange,
586:                            context.sanitizedContents);
587:                    r.setSource(source);
588:                    return r;
589:                } else {
590:                    return sanitize(context, sanitizing);
591:                }
592:            }
593:
594:            protected RubyParseResult createParseResult(ParserFile file,
595:                    AstTreeNode ast, Node root, RootNode realRoot,
596:                    RubyParserResult jrubyResult) {
597:                return new RubyParseResult(this , file, ast, root, realRoot,
598:                        jrubyResult);
599:            }
600:
601:            public PositionManager getPositionManager() {
602:                return positions;
603:            }
604:
605:            @SuppressWarnings("unchecked")
606:            public static RubyElement resolveHandle(CompilationInfo info,
607:                    ElementHandle handle) {
608:                if (handle instanceof  AstElement) {
609:                    AstElement element = (AstElement) handle;
610:                    CompilationInfo oldInfo = element.getInfo();
611:                    if (oldInfo == info) {
612:                        return element;
613:                    }
614:                    Node oldNode = element.getNode();
615:                    Node oldRoot = AstUtilities.getRoot(oldInfo);
616:
617:                    Node newRoot = AstUtilities.getRoot(info);
618:                    if (newRoot == null) {
619:                        return null;
620:                    }
621:
622:                    // Find newNode
623:                    Node newNode = find(oldRoot, oldNode, newRoot);
624:
625:                    if (newNode != null) {
626:                        AstElement co = AstElement.create(info, newNode);
627:
628:                        return co;
629:                    }
630:                } else if (handle instanceof  RubyElement) {
631:                    return (RubyElement) handle;
632:                }
633:
634:                return null;
635:            }
636:
637:            private static Node find(Node oldRoot, Node oldObject, Node newRoot) {
638:                // Walk down the tree to locate oldObject, and in the process, pick the same child for newRoot
639:                @SuppressWarnings("unchecked")
640:                List<? extends Node> oldChildren = oldRoot.childNodes();
641:                @SuppressWarnings("unchecked")
642:                List<? extends Node> newChildren = newRoot.childNodes();
643:                Iterator<? extends Node> itOld = oldChildren.iterator();
644:                Iterator<? extends Node> itNew = newChildren.iterator();
645:
646:                while (itOld.hasNext()) {
647:                    if (!itNew.hasNext()) {
648:                        return null; // No match - the trees have changed structure
649:                    }
650:
651:                    Node o = itOld.next();
652:                    Node n = itNew.next();
653:
654:                    if (o == oldObject) {
655:                        // Found it!
656:                        return n;
657:                    }
658:
659:                    // Recurse
660:                    Node match = find(o, oldObject, n);
661:
662:                    if (match != null) {
663:                        return match;
664:                    }
665:                }
666:
667:                if (itNew.hasNext()) {
668:                    return null; // No match - the trees have changed structure
669:                }
670:
671:                return null;
672:            }
673:
674:            /** Attempts to sanitize the input buffer */
675:            public static enum Sanitize {
676:                /** Only parse the current file accurately, don't try heuristics */
677:                NEVER,
678:                /** Perform no sanitization */
679:                NONE,
680:                /** Try to remove the trailing . or :: at the caret line */
681:                EDITED_DOT,
682:                /** Try to remove the trailing . or :: at the error position, or the prior
683:                 * line, or the caret line */
684:                ERROR_DOT,
685:                /** Try to remove the initial "if" or "unless" on the block
686:                 * in case it's not terminated
687:                 */
688:                BLOCK_START,
689:                /** Try to cut out the error line */
690:                ERROR_LINE,
691:                /** Try to cut out the current edited line, if known */
692:                EDITED_LINE,
693:                /** Attempt to add an "end" to the end of the buffer to make it compile */
694:                MISSING_END,
695:            }
696:
697:            /** Parsing context */
698:            public static class Context {
699:                private final ParserFile file;
700:                private final ParseListener listener;
701:                private int errorOffset;
702:                private String source;
703:                private String sanitizedSource;
704:                private OffsetRange sanitizedRange = OffsetRange.NONE;
705:                private String sanitizedContents;
706:                private int caretOffset;
707:                private Sanitize sanitized = Sanitize.NONE;
708:                private TranslatedSource translatedSource;
709:
710:                public Context(ParserFile parserFile, ParseListener listener,
711:                        String source, int caretOffset,
712:                        TranslatedSource translatedSource) {
713:                    this .file = parserFile;
714:                    this .listener = listener;
715:                    this .source = source;
716:                    this .caretOffset = caretOffset;
717:                    this .translatedSource = translatedSource;
718:                }
719:
720:                @Override
721:                public String toString() {
722:                    return "RubyParser.Context(" + file.toString() + ")"; // NOI18N
723:                }
724:
725:                public OffsetRange getSanitizedRange() {
726:                    return sanitizedRange;
727:                }
728:
729:                public Sanitize getSanitized() {
730:                    return sanitized;
731:                }
732:
733:                public String getSanitizedSource() {
734:                    return sanitizedSource;
735:                }
736:
737:                public int getErrorOffset() {
738:                    return errorOffset;
739:                }
740:            }
741:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.