Source Code Cross Referenced for RubyIndex.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) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:        package org.netbeans.modules.ruby;
0042:
0043:        import java.io.File;
0044:        import java.io.IOException;
0045:        import java.net.MalformedURLException;
0046:        import java.net.URL;
0047:        import java.util.ArrayList;
0048:        import java.util.Collections;
0049:        import java.util.EnumSet;
0050:        import java.util.HashMap;
0051:        import java.util.HashSet;
0052:        import java.util.Iterator;
0053:        import java.util.List;
0054:        import java.util.Map;
0055:        import java.util.Set;
0056:        import java.util.logging.Logger;
0057:        import java.util.regex.Pattern;
0058:
0059:        import org.netbeans.modules.gsf.api.Index;
0060:        import org.netbeans.modules.ruby.elements.IndexedField;
0061:        import static org.netbeans.modules.gsf.api.Index.*;
0062:        import org.netbeans.modules.gsf.api.NameKind;
0063:        import org.netbeans.api.ruby.platform.RubyPlatform;
0064:        import org.netbeans.api.ruby.platform.RubyPlatformManager;
0065:        import org.netbeans.modules.ruby.elements.IndexedClass;
0066:        import org.netbeans.modules.ruby.elements.IndexedElement;
0067:        import org.netbeans.modules.ruby.elements.IndexedMethod;
0068:        import org.openide.filesystems.FileObject;
0069:        import org.openide.filesystems.URLMapper;
0070:        import org.openide.modules.InstalledFileLocator;
0071:        import org.openide.util.Exceptions;
0072:
0073:        /**
0074:         * Access to the index of known Ruby classes - core, libraries, gems, user projects, etc.
0075:         *
0076:         * @todo Pull out attributes, fields and constants from the index as well
0077:         * @todo Store signature attributes for methods: private/protected?, documented?, returntype?
0078:         * @todo When there are multiple method/field definitions, pick access level from one which sets it
0079:         * @todo I do case-sensitive startsWith filtering here which is probably not good
0080:         * @todo Abort when search list .size() > N
0081:         * 
0082:         * @author Tor Norbye
0083:         */
0084:        public final class RubyIndex {
0085:
0086:            private static final Logger LOGGER = Logger
0087:                    .getLogger(RubyIndex.class.getName());
0088:
0089:            public static final String UNKNOWN_CLASS = "<Unknown>"; // NOI18N
0090:            public static final String OBJECT = "Object"; // NOI18N
0091:            private static final String CLASS = "Class"; // NOI18N
0092:            private static final String MODULE = "Module"; // NOI18N
0093:            static final Set<SearchScope> ALL_SCOPE = EnumSet
0094:                    .allOf(SearchScope.class);
0095:            static final Set<SearchScope> SOURCE_SCOPE = EnumSet
0096:                    .of(SearchScope.SOURCE);
0097:            private static String clusterUrl = null;
0098:            private static final String CLUSTER_URL = "cluster:"; // NOI18N
0099:            private static final String RUBYHOME_URL = "ruby:"; // NOI18N
0100:            private static final String GEM_URL = "gem:"; // NOI18N
0101:
0102:            private final Index index;
0103:
0104:            /** Creates a new instance of RubyIndex */
0105:            public RubyIndex(Index index) {
0106:                this .index = index;
0107:            }
0108:
0109:            public static RubyIndex get(Index index) {
0110:                return new RubyIndex(index);
0111:            }
0112:
0113:            private boolean search(String key, String name, NameKind kind,
0114:                    Set<SearchResult> result) {
0115:                try {
0116:                    index.search(key, name, kind, ALL_SCOPE, result, null);
0117:
0118:                    return true;
0119:                } catch (IOException ioe) {
0120:                    Exceptions.printStackTrace(ioe);
0121:
0122:                    return false;
0123:                }
0124:            }
0125:
0126:            private boolean search(String key, String name, NameKind kind,
0127:                    Set<SearchResult> result, Set<SearchScope> scope) {
0128:                try {
0129:                    index.search(key, name, kind, scope, result, null);
0130:
0131:                    return true;
0132:                } catch (IOException ioe) {
0133:                    Exceptions.printStackTrace(ioe);
0134:
0135:                    return false;
0136:                }
0137:            }
0138:
0139:            Set<IndexedClass> getClasses(String name, final NameKind kind,
0140:                    boolean includeAll, boolean skipClasses, boolean skipModules) {
0141:                return getClasses(name, kind, includeAll, skipClasses,
0142:                        skipModules, ALL_SCOPE, null);
0143:            }
0144:
0145:            /**
0146:             * Return the full set of classes that match the given name.
0147:             *
0148:             * @param name The name of the class - possibly a fqn like File::Stat, or just a class
0149:             *   name like Stat, or just a prefix like St.
0150:             * @param kind Whether we want the exact name, or whether we're searching by a prefix.
0151:             * @param includeAll If true, return multiple IndexedClasses for the same logical
0152:             *   class, one for each declaration point. For example, File is defined both in the
0153:             *   builtin stubs as well as in ftools.
0154:             */
0155:            public Set<IndexedClass> getClasses(String name,
0156:                    final NameKind kind, boolean includeAll,
0157:                    boolean skipClasses, boolean skipModules,
0158:                    Set<Index.SearchScope> scope, Set<String> uniqueClasses) {
0159:                String classFqn = null;
0160:
0161:                if (name != null) {
0162:                    if (name.indexOf("::") != -1) { // NOI18N
0163:
0164:                        int p = name.lastIndexOf("::"); // NOI18N
0165:                        classFqn = name.substring(0, p);
0166:                        name = name.substring(p + 2);
0167:                    } else if (name.endsWith(":")) {
0168:                        // User has typed something like "Test:" and wants completion on
0169:                        // for something like Test::Unit
0170:                        classFqn = name.substring(0, name.length() - 1);
0171:                        name = "";
0172:                    }
0173:                }
0174:
0175:                final Set<SearchResult> result = new HashSet<SearchResult>();
0176:
0177:                //        if (!isValid()) {
0178:                //            LOGGER.fine(String.format("LuceneIndex[%s] is invalid!\n", this.toString()));
0179:                //            return;
0180:                //        }
0181:                String field;
0182:
0183:                switch (kind) {
0184:                case EXACT_NAME:
0185:                case PREFIX:
0186:                case CAMEL_CASE:
0187:                case REGEXP:
0188:                    field = RubyIndexer.FIELD_CLASS_NAME;
0189:
0190:                    break;
0191:
0192:                case CASE_INSENSITIVE_PREFIX:
0193:                case CASE_INSENSITIVE_REGEXP:
0194:                    field = RubyIndexer.FIELD_CASE_INSENSITIVE_CLASS_NAME;
0195:
0196:                    break;
0197:
0198:                default:
0199:                    throw new UnsupportedOperationException(kind.toString());
0200:                }
0201:
0202:                search(field, name, kind, result, scope);
0203:
0204:                // TODO Prune methods to fit my scheme - later make lucene index smarter about how to prune its index search
0205:                if (includeAll) {
0206:                    uniqueClasses = null;
0207:                } else if (uniqueClasses == null) {
0208:                    uniqueClasses = new HashSet<String>();
0209:                }
0210:
0211:                final Set<IndexedClass> classes = new HashSet<IndexedClass>();
0212:
0213:                for (SearchResult map : result) {
0214:                    String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME);
0215:
0216:                    if (clz == null) {
0217:                        // It's probably a module
0218:                        // XXX I need to handle this... for now punt
0219:                        continue;
0220:                    }
0221:
0222:                    // Lucene returns some inexact matches, TODO investigate why this is necessary
0223:                    if ((kind == NameKind.PREFIX) && !clz.startsWith(name)) {
0224:                        continue;
0225:                    } else if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0226:                            && !clz.regionMatches(true, 0, name, 0, name
0227:                                    .length())) {
0228:                        continue;
0229:                    }
0230:
0231:                    if (classFqn != null) {
0232:                        if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0233:                                || kind == NameKind.CASE_INSENSITIVE_REGEXP) {
0234:                            if (!classFqn.equalsIgnoreCase(map
0235:                                    .getValue(RubyIndexer.FIELD_IN))) {
0236:                                continue;
0237:                            }
0238:                        } else if (kind == NameKind.CAMEL_CASE) {
0239:                            String in = map.getValue(RubyIndexer.FIELD_IN);
0240:                            if (in != null) {
0241:                                // Superslow, make faster 
0242:                                StringBuilder sb = new StringBuilder();
0243:                                //                        String prefix = null;
0244:                                int lastIndex = 0;
0245:                                int index;
0246:                                do {
0247:
0248:                                    int nextUpper = -1;
0249:                                    for (int i = lastIndex + 1; i < classFqn
0250:                                            .length(); i++) {
0251:                                        if (Character.isUpperCase(classFqn
0252:                                                .charAt(i))) {
0253:                                            nextUpper = i;
0254:                                            break;
0255:                                        }
0256:                                    }
0257:                                    index = nextUpper;
0258:                                    String token = classFqn.substring(
0259:                                            lastIndex, index == -1 ? classFqn
0260:                                                    .length() : index);
0261:                                    //                            if ( lastIndex == 0 ) {
0262:                                    //                                prefix = token;
0263:                                    //                            }
0264:                                    sb.append(token);
0265:                                    // TODO - add in Ruby chars here?
0266:                                    sb
0267:                                            .append(index != -1 ? "[\\p{javaLowerCase}\\p{Digit}_\\$]*"
0268:                                                    : ".*"); // NOI18N         
0269:                                    lastIndex = index;
0270:                                } while (index != -1);
0271:
0272:                                final Pattern pattern = Pattern.compile(sb
0273:                                        .toString());
0274:                                if (!pattern.matcher(in).matches()) {
0275:                                    continue;
0276:                                }
0277:                            } else {
0278:                                continue;
0279:                            }
0280:                        } else {
0281:                            if (!classFqn.equals(map
0282:                                    .getValue(RubyIndexer.FIELD_IN))) {
0283:                                continue;
0284:                            }
0285:                        }
0286:                    }
0287:
0288:                    String attrs = map.getValue(RubyIndexer.FIELD_CLASS_ATTRS);
0289:                    boolean isClass = true;
0290:                    if (attrs != null) {
0291:                        int flags = IndexedElement.stringToFlag(attrs, 0);
0292:                        isClass = (flags & IndexedClass.MODULE) == 0;
0293:
0294:                    }
0295:
0296:                    if (skipClasses && isClass) {
0297:                        continue;
0298:                    }
0299:
0300:                    if (skipModules && !isClass) {
0301:                        continue;
0302:                    }
0303:
0304:                    String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0305:
0306:                    // Only return a single instance for this signature
0307:                    if (!includeAll) {
0308:                        if (uniqueClasses.contains(fqn)) { // use a map to point right to the class
0309:                            // Prefer the instance that provides documentation
0310:
0311:                            boolean replaced = false;
0312:
0313:                            int flags = 0;
0314:                            if (attrs != null) {
0315:                                flags = IndexedElement.stringToFlag(attrs, 0);
0316:                            }
0317:
0318:                            boolean isDocumented = (flags & IndexedElement.DOCUMENTED) != 0;
0319:
0320:                            if (isDocumented) {
0321:                                // Check the actual size of the documentation, and prefer the largest
0322:                                // method
0323:                                int length = 0;
0324:                                int documentedAt = attrs.indexOf(';');
0325:
0326:                                if (documentedAt != -1) {
0327:                                    int end = attrs.indexOf(';',
0328:                                            documentedAt + 1);
0329:                                    if (end == -1) {
0330:                                        end = attrs.length();
0331:                                    }
0332:                                    length = Integer.parseInt(attrs.substring(
0333:                                            documentedAt + 1, end));
0334:                                }
0335:
0336:                                // This instance is documented. Replace the other instance...
0337:                                for (IndexedClass c : classes) {
0338:                                    if (c.getSignature().equals(fqn)
0339:                                            && (length > c
0340:                                                    .getDocumentationLength())) {
0341:                                        classes.remove(c);
0342:                                        replaced = true;
0343:
0344:                                        break;
0345:                                    }
0346:                                }
0347:                            }
0348:
0349:                            if (!replaced) {
0350:                                continue;
0351:                            }
0352:                        } else {
0353:                            uniqueClasses.add(fqn);
0354:                        }
0355:                    }
0356:
0357:                    classes.add(createClass(fqn, clz, map));
0358:                }
0359:
0360:                return classes;
0361:            }
0362:
0363:            /**
0364:             * Return the set of classes that directly subclass the given class
0365:             *
0366:             * @param name The name of the class - possibly a fqn like File::Stat, or just a class
0367:             *   name like Stat, or just a prefix like St.
0368:             * @param kind Whether we want the exact name, or whether we're searching by a prefix.
0369:             * @param includeAll If true, return multiple IndexedClasses for the same logical
0370:             *   class, one for each declaration point. For example, File is defined both in the
0371:             *   builtin stubs as well as in ftools.
0372:             */
0373:            public Set<IndexedClass> getSubClasses(String name, String fqn,
0374:                    final NameKind kind, Set<Index.SearchScope> scope) {
0375:                final Set<SearchResult> result = new HashSet<SearchResult>();
0376:
0377:                //        if (!isValid()) {
0378:                //            LOGGER.fine(String.format("LuceneIndex[%s] is invalid!\n", this.toString()));
0379:                //            return;
0380:                //        }
0381:                String field = RubyIndexer.FIELD_EXTENDS_NAME;
0382:                search(field, fqn, NameKind.EXACT_NAME, result, scope);
0383:
0384:                final Set<IndexedClass> classes = new HashSet<IndexedClass>();
0385:
0386:                for (SearchResult map : result) {
0387:                    String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME);
0388:
0389:                    if (clz == null) {
0390:                        // It's probably a module
0391:                        // XXX I need to handle this... for now punt
0392:                        continue;
0393:                    }
0394:
0395:                    // Lucene returns some inexact matches, TODO investigate why this is necessary
0396:                    if ((kind == NameKind.PREFIX) && !clz.startsWith(name)) {
0397:                        continue;
0398:                    } else if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0399:                            && !clz.regionMatches(true, 0, name, 0, name
0400:                                    .length())) {
0401:                        continue;
0402:                    }
0403:
0404:                    String cfqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0405:
0406:                    // Only return a single instance for this signature
0407:                    classes.add(createClass(cfqn, clz, map));
0408:                }
0409:
0410:                return classes;
0411:            }
0412:
0413:            /**
0414:             * Return a set of methods that match the given name prefix, and are in the given
0415:             * class and module. If no class is specified, match methods across all classes.
0416:             * Note that inherited methods are not checked. If you want to match inherited methods
0417:             * you must call this method on each superclass as well as the mixin modules.
0418:             */
0419:            @SuppressWarnings("unchecked")
0420:            // unchecked - lucene has source 1.4
0421:            Set<IndexedMethod> getMethods(final String name, final String clz,
0422:                    NameKind kind) {
0423:                return getMethods(name, clz, kind, ALL_SCOPE);
0424:            }
0425:
0426:            @SuppressWarnings("fallthrough")
0427:            public Set<IndexedMethod> getMethods(final String name,
0428:                    final String clz, NameKind kind,
0429:                    Set<Index.SearchScope> scope) {
0430:                boolean inherited = clz == null;
0431:
0432:                //    public void searchByCriteria(final String name, final ClassIndex.NameKind kind, /*final ResultConvertor<T> convertor,*/ final Set<String> result) throws IOException {
0433:                final Set<SearchResult> result = new HashSet<SearchResult>();
0434:
0435:                //        if (!isValid()) {
0436:                //            LOGGER.fine(String.format("LuceneIndex[%s] is invalid!\n", this.toString()));
0437:                //            return;
0438:                //        }
0439:                String field = RubyIndexer.FIELD_METHOD_NAME;
0440:                NameKind originalKind = kind;
0441:                if (kind == NameKind.EXACT_NAME) {
0442:                    // I can't do exact searches on methods because the method
0443:                    // entries include signatures etc. So turn this into a prefix
0444:                    // search and then compare chopped off signatures with the name
0445:                    kind = NameKind.PREFIX;
0446:                }
0447:
0448:                // No point in doing case insensitive searches on method names because
0449:                // method names in Ruby are always case insensitive anyway
0450:                //            case CASE_INSENSITIVE_PREFIX:
0451:                //            case CASE_INSENSITIVE_REGEXP:
0452:                //                field = RubyIndexer.FIELD_CASE_INSENSITIVE_METHOD_NAME;
0453:                //                break;
0454:
0455:                search(field, name, kind, result, scope);
0456:
0457:                //return Collections.unmodifiableSet(result);
0458:
0459:                // TODO Prune methods to fit my scheme - later make lucene index smarter about how to prune its index search
0460:                final Set<IndexedMethod> methods = new HashSet<IndexedMethod>();
0461:
0462:                for (SearchResult map : result) {
0463:                    if (clz != null) {
0464:                        String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0465:
0466:                        if (!(clz.equals(fqn))) {
0467:                            continue;
0468:                        }
0469:                    }
0470:
0471:                    String[] signatures = map
0472:                            .getValues(RubyIndexer.FIELD_METHOD_NAME);
0473:
0474:                    if (signatures != null) {
0475:                        for (String signature : signatures) {
0476:                            // Skip weird methods... Think harder about this
0477:                            if (((name == null) || (name.length() == 0))
0478:                                    && !Character.isLowerCase(signature
0479:                                            .charAt(0))) {
0480:                                continue;
0481:                            }
0482:
0483:                            // Lucene returns some inexact matches, TODO investigate why this is necessary
0484:                            if ((kind == NameKind.PREFIX)
0485:                                    && !signature.startsWith(name)) {
0486:                                continue;
0487:                            } else if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0488:                                    && !signature.regionMatches(true, 0, name,
0489:                                            0, name.length())) {
0490:                                continue;
0491:                            } else if (kind == NameKind.CASE_INSENSITIVE_REGEXP) {
0492:                                int len = signature.length();
0493:                                int end = signature.indexOf('(');
0494:                                if (end == -1) {
0495:                                    end = signature.indexOf(';');
0496:                                    if (end == -1) {
0497:                                        end = len;
0498:                                    }
0499:                                }
0500:                                String n = end != len ? signature.substring(0,
0501:                                        end) : signature;
0502:                                try {
0503:                                    if (!n.matches(name)) {
0504:                                        continue;
0505:                                    }
0506:                                } catch (Exception e) {
0507:                                    // Silently ignore regexp failures in the search expression
0508:                                }
0509:                            } else if (originalKind == NameKind.EXACT_NAME) {
0510:                                // Make sure the name matches exactly
0511:                                // We know that the prefix is correct from the first part of
0512:                                // this if clause, by the signature may have more
0513:                                if (((signature.length() > name.length()) && (signature
0514:                                        .charAt(name.length()) != '('))
0515:                                        && (signature.charAt(name.length()) != ';')) {
0516:                                    continue;
0517:                                }
0518:                            }
0519:
0520:                            // XXX THIS DOES NOT WORK WHEN THERE ARE IDENTICAL SIGNATURES!!!
0521:                            assert map != null;
0522:                            methods
0523:                                    .add(createMethod(signature, map, inherited));
0524:                        }
0525:                    }
0526:
0527:                    String[] attributes = map
0528:                            .getValues(RubyIndexer.FIELD_ATTRIBUTE_NAME);
0529:
0530:                    if (attributes != null) {
0531:                        for (String signature : attributes) {
0532:                            // Skip weird methods... Think harder about this
0533:                            if (((name == null) || (name.length() == 0))
0534:                                    && !Character.isLowerCase(signature
0535:                                            .charAt(0))) {
0536:                                continue;
0537:                            }
0538:
0539:                            // Lucene returns some inexact matches, TODO investigate why this is necessary
0540:                            if (kind == NameKind.PREFIX
0541:                                    && !signature.startsWith(name)) {
0542:                                continue;
0543:                            } else if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0544:                                    && !signature.regionMatches(true, 0, name,
0545:                                            0, name.length())) {
0546:                                continue;
0547:                            } else if (kind == NameKind.CASE_INSENSITIVE_REGEXP
0548:                                    && !signature.matches(name)) {
0549:                                continue;
0550:                            } else if (originalKind == NameKind.EXACT_NAME) {
0551:                                // Make sure the name matches exactly
0552:                                // We know that the prefix is correct from the first part of
0553:                                // this if clause, by the signature may have more
0554:                                if (((signature.length() > name.length()) &&
0555:                                //(signature.charAt(name.length()) != '(')) &&
0556:                                (signature.charAt(name.length()) != ';'))) {
0557:                                    continue;
0558:                                }
0559:                            }
0560:
0561:                            // XXX THIS DOES NOT WORK WHEN THERE ARE IDENTICAL SIGNATURES!!!
0562:                            assert map != null;
0563:                            // Create method for the attribute
0564:                            methods
0565:                                    .add(createMethod(signature, map, inherited));
0566:                        }
0567:                    }
0568:
0569:                    // TODO - fields
0570:                }
0571:
0572:                return methods;
0573:            }
0574:
0575:            private IndexedMethod createMethod(String signature,
0576:                    SearchResult map, boolean inherited) {
0577:                String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME);
0578:                String module = map.getValue(RubyIndexer.FIELD_IN);
0579:
0580:                if (clz == null) {
0581:                    // Module method?
0582:                    clz = module;
0583:                } else if ((module != null) && (module.length() > 0)) {
0584:                    clz = module + "::" + clz;
0585:                }
0586:
0587:                String fileUrl = map.getPersistentUrl();
0588:
0589:                String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0590:                String require = map.getValue(RubyIndexer.FIELD_REQUIRE);
0591:
0592:                // Extract attributes
0593:                int attributeIndex = signature.indexOf(';');
0594:                String attributes = null;
0595:                int flags = 0;
0596:
0597:                if (attributeIndex != -1) {
0598:                    flags = IndexedElement.stringToFlag(signature,
0599:                            attributeIndex + 1);
0600:
0601:                    if (signature.length() > attributeIndex + 1) {
0602:                        attributes = signature.substring(attributeIndex + 1,
0603:                                signature.length());
0604:                    }
0605:
0606:                    signature = signature.substring(0, attributeIndex);
0607:                }
0608:
0609:                IndexedMethod m = IndexedMethod.create(this , signature, fqn,
0610:                        clz, fileUrl, require, attributes, flags);
0611:
0612:                m.setInherited(inherited);
0613:                return m;
0614:            }
0615:
0616:            private IndexedField createField(String signature,
0617:                    SearchResult map, boolean isInstance, boolean inherited) {
0618:                String clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME);
0619:                String module = map.getValue(RubyIndexer.FIELD_IN);
0620:
0621:                if (clz == null) {
0622:                    // Module method?
0623:                    clz = module;
0624:                } else if ((module != null) && (module.length() > 0)) {
0625:                    clz = module + "::" + clz;
0626:                }
0627:
0628:                String fileUrl = map.getPersistentUrl();
0629:
0630:                String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0631:                String require = map.getValue(RubyIndexer.FIELD_REQUIRE);
0632:
0633:                int attributeIndex = signature.indexOf(';');
0634:                String attributes = null;
0635:                int flags = 0;
0636:
0637:                if (attributeIndex != -1) {
0638:                    flags = IndexedElement.stringToFlag(signature,
0639:                            attributeIndex + 1);
0640:
0641:                    if (signature.length() > attributeIndex + 1) {
0642:                        attributes = signature.substring(attributeIndex + 1,
0643:                                signature.length());
0644:                    }
0645:
0646:                    signature = signature.substring(0, attributeIndex);
0647:                }
0648:
0649:                IndexedField m = IndexedField.create(this , signature, fqn, clz,
0650:                        fileUrl, require, attributes, flags);
0651:                m.setInherited(inherited);
0652:
0653:                return m;
0654:            }
0655:
0656:            private IndexedClass createClass(String fqn, String clz,
0657:                    SearchResult map) {
0658:                String require = map.getValue(RubyIndexer.FIELD_REQUIRE);
0659:
0660:                // TODO - how do I determine -which- file to associate with the file?
0661:                // Perhaps the one that defines initialize() ?
0662:                String fileUrl = map.getPersistentUrl();
0663:
0664:                if (clz == null) {
0665:                    clz = map.getValue(RubyIndexer.FIELD_CLASS_NAME);
0666:                }
0667:
0668:                String attrs = map.getValue(RubyIndexer.FIELD_CLASS_ATTRS);
0669:
0670:                int flags = 0;
0671:                if (attrs != null) {
0672:                    flags = IndexedElement.stringToFlag(attrs, 0);
0673:                }
0674:
0675:                IndexedClass c = IndexedClass.create(this , clz, fqn, fileUrl,
0676:                        require, attrs, flags);
0677:
0678:                return c;
0679:            }
0680:
0681:            // List of String[2]: 0: requirename, 1: fqn
0682:            public Set<String[]> getRequires(final String name,
0683:                    final NameKind kind) {
0684:                final Set<SearchResult> result = new HashSet<SearchResult>();
0685:
0686:                String field = RubyIndexer.FIELD_REQUIRE;
0687:
0688:                search(field, name, kind, result);
0689:
0690:                // TODO Prune methods to fit my scheme - later make lucene index smarter about how to prune its index search
0691:                final Map<String, String> fqns = new HashMap<String, String>();
0692:
0693:                for (SearchResult map : result) {
0694:                    String[] r = map.getValues(field);
0695:
0696:                    if (r != null) {
0697:                        for (String require : r) {
0698:                            // Lucene returns some inexact matches, TODO investigate why this is necessary
0699:                            if (kind == NameKind.PREFIX
0700:                                    && !require.startsWith(name)) {
0701:                                continue;
0702:                            } else if (kind == NameKind.CASE_INSENSITIVE_PREFIX
0703:                                    && !require.regionMatches(true, 0, name, 0,
0704:                                            name.length())) {
0705:                                continue;
0706:                            }
0707:                            assert map != null;
0708:
0709:                            // TODO - check if there's a rubygem which captures this
0710:                            // require and if so, remove it
0711:                            String fqn = map
0712:                                    .getValue(RubyIndexer.FIELD_FQN_NAME);
0713:
0714:                            String there = fqns.get(require);
0715:
0716:                            if ((fqn != null)
0717:                                    && ((there == null) || ((there != null) && (there
0718:                                            .length() < fqn.length())))) {
0719:                                fqns.put(require, fqn);
0720:                            }
0721:                        }
0722:                    }
0723:                }
0724:
0725:                final Set<String[]> requires = new HashSet<String[]>();
0726:
0727:                for (String require : fqns.keySet()) {
0728:                    String fqn = fqns.get(require);
0729:                    String[] item = new String[2];
0730:                    item[0] = require;
0731:                    item[1] = fqn;
0732:                    requires.add(item);
0733:                }
0734:
0735:                return requires;
0736:            }
0737:
0738:            public Set<String> getRequiresTransitively(Set<String> requires) {
0739:                // Not yet implemented - this requires me to index the require-statements in the files
0740:                return requires;
0741:            }
0742:
0743:            // List of String[2]: 0: requirename, 1: fqn
0744:            public Set<String> getClassesIn(final String require) {
0745:                final Set<SearchResult> result = new HashSet<SearchResult>();
0746:
0747:                String field = RubyIndexer.FIELD_REQUIRE;
0748:
0749:                search(field, require, NameKind.EXACT_NAME, result);
0750:
0751:                final Set<String> fqns = new HashSet<String>();
0752:
0753:                for (SearchResult map : result) {
0754:                    String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0755:
0756:                    if (fqn != null) {
0757:                        fqns.add(fqn);
0758:                    }
0759:                }
0760:
0761:                return fqns;
0762:            }
0763:
0764:            public IndexedClass getSuperclass(String fqn) {
0765:                final Set<SearchResult> result = new HashSet<SearchResult>();
0766:
0767:                NameKind kind = NameKind.EXACT_NAME;
0768:                String field = RubyIndexer.FIELD_FQN_NAME;
0769:
0770:                search(field, fqn, kind, result);
0771:
0772:                // XXX Uhm... there could be multiple... Shouldn't I return a set here?
0773:                // (e.g. you can have your own class named File which has nothing to
0774:                // do with the builtin, and has a separate super class...
0775:
0776:                for (SearchResult map : result) {
0777:                    assert fqn.equals(map.getValue(RubyIndexer.FIELD_FQN_NAME));
0778:
0779:                    String extendsClass = map
0780:                            .getValue(RubyIndexer.FIELD_EXTENDS_NAME);
0781:
0782:                    if (extendsClass != null) {
0783:                        // Found the class name, now look it up in the index
0784:                        result.clear();
0785:
0786:                        if (!search(field, extendsClass, kind, result)) {
0787:                            return null;
0788:                        }
0789:
0790:                        // There should be exactly one match
0791:                        if (result.size() > 0) {
0792:                            SearchResult super Map = result.iterator().next();
0793:                            String super Fqn = super Map
0794:                                    .getValue(RubyIndexer.FIELD_FQN_NAME);
0795:
0796:                            return createClass(super Fqn, extendsClass, super Map);
0797:                        } else {
0798:                            return null;
0799:                        }
0800:                    }
0801:                }
0802:
0803:                return null;
0804:            }
0805:
0806:            private boolean addSubclasses(String classFqn,
0807:                    Set<IndexedClass> classes, Set<String> seenClasses,
0808:                    Set<String> scannedClasses, boolean directOnly) {
0809:                // Prevent problems with circular includes or redundant includes
0810:                if (scannedClasses.contains(classFqn)) {
0811:                    return false;
0812:                }
0813:
0814:                scannedClasses.add(classFqn);
0815:
0816:                String searchField = RubyIndexer.FIELD_EXTENDS_NAME;
0817:
0818:                Set<SearchResult> result = new HashSet<SearchResult>();
0819:
0820:                search(searchField, classFqn, NameKind.EXACT_NAME, result);
0821:
0822:                boolean foundIt = result.size() > 0;
0823:
0824:                // If this is a bogus class entry (no search rsults) don't continue
0825:                if (!foundIt) {
0826:                    return foundIt;
0827:                }
0828:
0829:                for (SearchResult map : result) {
0830:                    String fqn = map.getValue(RubyIndexer.FIELD_FQN_NAME);
0831:                    if (!seenClasses.contains(fqn)) {
0832:                        IndexedClass clz = createClass(fqn, null, map);
0833:                        classes.add(clz);
0834:                        seenClasses.add(fqn);
0835:
0836:                        if (!directOnly) {
0837:                            addSubclasses(fqn, classes, seenClasses,
0838:                                    scannedClasses, directOnly);
0839:                        }
0840:                    }
0841:                }
0842:
0843:                return foundIt;
0844:            }
0845:
0846:            /** Find the subclasses of the given class name, with the POSSIBLE fqn from the
0847:             * context of the usage. */
0848:            public Set<IndexedClass> getSubClasses(String fqn,
0849:                    String possibleFqn, String name, boolean directOnly) {
0850:                //String field = RubyIndexer.FIELD_FQN_NAME;
0851:                Set<IndexedClass> classes = new HashSet<IndexedClass>();
0852:                Set<String> scannedClasses = new HashSet<String>();
0853:                Set<String> seenClasses = new HashSet<String>();
0854:
0855:                if (fqn != null) {
0856:                    addSubclasses(fqn, classes, seenClasses, scannedClasses,
0857:                            directOnly);
0858:                } else {
0859:                    fqn = possibleFqn;
0860:
0861:                    // Try looking at the libraries too
0862:                    while ((classes.size() == 0) && (fqn.length() > 0)) {
0863:                        // TODO - use the boolvalue from addclasses instead!
0864:                        boolean found = addSubclasses(fqn + "::" + name,
0865:                                classes, seenClasses, scannedClasses,
0866:                                directOnly);
0867:                        if (found) {
0868:                            return classes;
0869:                        }
0870:
0871:                        int f = fqn.lastIndexOf("::");
0872:
0873:                        if (f == -1) {
0874:                            break;
0875:                        } else {
0876:                            fqn = fqn.substring(0, f);
0877:                        }
0878:                    }
0879:
0880:                    if (classes.size() == 0) {
0881:                        addSubclasses(name, classes, seenClasses,
0882:                                scannedClasses, directOnly);
0883:                    }
0884:                }
0885:
0886:                return classes;
0887:            }
0888:
0889:            /** Return the most distant method in the hierarchy that is overriding the given method, or null
0890:             * @todo Make this method actually compute most distant ancestor
0891:             * @todo Use arglist arity comparison to reject methods that are not overrides...
0892:             */
0893:            public IndexedMethod getOverridingMethod(String className,
0894:                    String methodName) {
0895:                Set<IndexedMethod> methods = getInheritedMethods(className,
0896:                        methodName, NameKind.EXACT_NAME);
0897:
0898:                // TODO - this is only returning ONE match, not the most distant one. I really need to
0899:                // produce a RubyIndex method for this which can walk in there and do a decent job!
0900:
0901:                for (IndexedMethod method : methods) {
0902:                    // getInheritedMethods may return methods ON fqn itself
0903:                    if (!method.getIn().equals(className)) {
0904:                        return method;
0905:                    }
0906:                }
0907:
0908:                return null;
0909:            }
0910:
0911:            /**
0912:             * Get the set of inherited (through super classes and mixins) for the given fully qualified class name.
0913:             * @param classFqn FQN: module1::module2::moduleN::class
0914:             * @param prefix If kind is NameKind.PREFIX/CASE_INSENSITIVE_PREFIX, a prefix to filter methods by. Else,
0915:             *    if kind is NameKind.EXACT_NAME filter methods by the exact name.
0916:             * @param kind Whether the prefix field should be taken as a prefix or a whole name
0917:             */
0918:            public Set<IndexedMethod> getInheritedMethods(String classFqn,
0919:                    String prefix, NameKind kind) {
0920:                boolean haveRedirected = false;
0921:
0922:                if ((classFqn == null) || classFqn.equals(OBJECT)) {
0923:                    // Redirect inheritance tree to Class to pick up methods in Class and Module
0924:                    classFqn = CLASS;
0925:                    haveRedirected = true;
0926:                } else if (MODULE.equals(classFqn) || CLASS.equals(classFqn)) {
0927:                    haveRedirected = true;
0928:                }
0929:
0930:                //String field = RubyIndexer.FIELD_FQN_NAME;
0931:                Set<IndexedMethod> methods = new HashSet<IndexedMethod>();
0932:                Set<String> scannedClasses = new HashSet<String>();
0933:                Set<String> seenSignatures = new HashSet<String>();
0934:
0935:                if (prefix == null) {
0936:                    prefix = "";
0937:                }
0938:
0939:                addMethodsFromClass(prefix, kind, classFqn, methods,
0940:                        seenSignatures, scannedClasses, haveRedirected, false);
0941:
0942:                return methods;
0943:            }
0944:
0945:            /** Return whether the specific class referenced (classFqn) was found or not. This is
0946:             * not the same as returning whether any classes were added since it may add
0947:             * additional methods from parents (Object/Class).
0948:             */
0949:            private boolean addMethodsFromClass(String prefix, NameKind kind,
0950:                    String classFqn, Set<IndexedMethod> methods,
0951:                    Set<String> seenSignatures, Set<String> scannedClasses,
0952:                    boolean haveRedirected, boolean inheriting) {
0953:                // Prevent problems with circular includes or redundant includes
0954:                if (scannedClasses.contains(classFqn)) {
0955:                    return false;
0956:                }
0957:
0958:                scannedClasses.add(classFqn);
0959:
0960:                String searchField = RubyIndexer.FIELD_FQN_NAME;
0961:
0962:                Set<SearchResult> result = new HashSet<SearchResult>();
0963:
0964:                search(searchField, classFqn, NameKind.EXACT_NAME, result);
0965:
0966:                boolean foundIt = result.size() > 0;
0967:
0968:                // If this is a bogus class entry (no search rsults) don't continue
0969:                if (!foundIt) {
0970:                    return foundIt;
0971:                }
0972:
0973:                String extendsClass = null;
0974:
0975:                String classIn = null;
0976:                int fqnIndex = classFqn.lastIndexOf("::"); // NOI18N
0977:
0978:                if (fqnIndex != -1) {
0979:                    classIn = classFqn.substring(0, fqnIndex);
0980:                }
0981:
0982:                for (SearchResult map : result) {
0983:                    assert map != null;
0984:
0985:                    if (extendsClass == null) {
0986:                        extendsClass = map
0987:                                .getValue(RubyIndexer.FIELD_EXTENDS_NAME);
0988:                    }
0989:
0990:                    String includes = map.getValue(RubyIndexer.FIELD_INCLUDES);
0991:
0992:                    if (includes != null) {
0993:                        String[] in = includes.split(",");
0994:
0995:                        // I have Util::BacktraceFilter and Assertions, which are both
0996:                        // relative to ::,Test,Test::Unit
0997:                        for (String include : in) {
0998:                            // Try both with and without a package qualifier
0999:                            boolean isQualified = false;
1000:
1001:                            if (classIn != null) {
1002:                                isQualified = addMethodsFromClass(prefix, kind,
1003:                                        classIn + "::" + include, methods,
1004:                                        seenSignatures, scannedClasses,
1005:                                        haveRedirected, true);
1006:                            }
1007:
1008:                            if (!isQualified) {
1009:                                addMethodsFromClass(prefix, kind, include,
1010:                                        methods, seenSignatures,
1011:                                        scannedClasses, haveRedirected, true);
1012:                            }
1013:                        }
1014:                    }
1015:
1016:                    String extendWith = map
1017:                            .getValue(RubyIndexer.FIELD_EXTEND_WITH);
1018:
1019:                    if (extendWith != null) {
1020:                        // Try both with and without a package qualifier
1021:                        boolean isQualified = false;
1022:
1023:                        if (classIn != null) {
1024:                            isQualified = addMethodsFromClass(prefix, kind,
1025:                                    classIn + "::" + extendWith, methods,
1026:                                    seenSignatures, scannedClasses,
1027:                                    haveRedirected, true);
1028:                        }
1029:
1030:                        if (!isQualified) {
1031:                            addMethodsFromClass(prefix, kind, extendWith,
1032:                                    methods, seenSignatures, scannedClasses,
1033:                                    haveRedirected, true);
1034:                        }
1035:                    }
1036:
1037:                    String[] signatures = map
1038:                            .getValues(RubyIndexer.FIELD_METHOD_NAME);
1039:
1040:                    if (signatures != null) {
1041:                        for (String signature : signatures) {
1042:                            // Skip weird methods like "[]" etc. in completion lists... TODO Think harder about this
1043:                            if ((prefix.length() == 0)
1044:                                    && !Character.isLowerCase(signature
1045:                                            .charAt(0))) {
1046:                                continue;
1047:                            }
1048:
1049:                            // Prevent duplicates when method is redefined
1050:                            if (!seenSignatures.contains(signature)) {
1051:                                if (signature.startsWith(prefix)) {
1052:                                    if (kind == NameKind.EXACT_NAME) {
1053:                                        // Ensure that the method is not longer than the prefix
1054:                                        if ((signature.length() > prefix
1055:                                                .length())
1056:                                                && (signature.charAt(prefix
1057:                                                        .length()) != '(')
1058:                                                && (signature.charAt(prefix
1059:                                                        .length()) != ';')) {
1060:                                            continue;
1061:                                        }
1062:                                    } else {
1063:                                        // REGEXP, CAMELCASE filtering etc. not supported here
1064:                                        assert (kind == NameKind.PREFIX)
1065:                                                || (kind == NameKind.CASE_INSENSITIVE_PREFIX);
1066:                                    }
1067:
1068:                                    seenSignatures.add(signature);
1069:
1070:                                    IndexedMethod method = createMethod(
1071:                                            signature, map, inheriting);
1072:                                    method.setSmart(!haveRedirected);
1073:                                    methods.add(method);
1074:                                }
1075:                            }
1076:                        }
1077:                    }
1078:
1079:                    String[] attributes = map
1080:                            .getValues(RubyIndexer.FIELD_ATTRIBUTE_NAME);
1081:
1082:                    if (attributes != null) {
1083:                        for (String attribute : attributes) {
1084:                            // Skip weird methods like "[]" etc. in completion lists... TODO Think harder about this
1085:                            if ((prefix.length() == 0)
1086:                                    && !Character.isLowerCase(attribute
1087:                                            .charAt(0))) {
1088:                                continue;
1089:                            }
1090:
1091:                            // Prevent duplicates when method is redefined
1092:                            if (!seenSignatures.contains(attribute)) {
1093:                                if (attribute.startsWith(prefix)) {
1094:                                    if (kind == NameKind.EXACT_NAME) {
1095:                                        // Ensure that the method is not longer than the prefix
1096:                                        if ((attribute.length() > prefix
1097:                                                .length())
1098:                                                && (attribute.charAt(prefix
1099:                                                        .length()) != '(')
1100:                                                && (attribute.charAt(prefix
1101:                                                        .length()) != ';')) {
1102:                                            continue;
1103:                                        }
1104:                                    } else {
1105:                                        // REGEXP, CAMELCASE filtering etc. not supported here
1106:                                        assert (kind == NameKind.PREFIX)
1107:                                                || (kind == NameKind.CASE_INSENSITIVE_PREFIX);
1108:                                    }
1109:
1110:                                    seenSignatures.add(attribute);
1111:
1112:                                    // TODO - create both getter and setter methods
1113:                                    IndexedMethod method = createMethod(
1114:                                            attribute, map, inheriting);
1115:                                    method.setSmart(!haveRedirected);
1116:                                    method
1117:                                            .setMethodType(IndexedMethod.MethodType.ATTRIBUTE);
1118:                                    methods.add(method);
1119:                                }
1120:                            }
1121:                        }
1122:                    }
1123:                }
1124:
1125:                if (classFqn.equals(OBJECT)) {
1126:                    return foundIt;
1127:                }
1128:
1129:                if (extendsClass == null) {
1130:                    if (haveRedirected) {
1131:                        addMethodsFromClass(prefix, kind, OBJECT, methods,
1132:                                seenSignatures, scannedClasses, true, true);
1133:                    } else {
1134:                        // Rather than inheriting directly from object,
1135:                        // let's go via Class (and Module) up to Object
1136:                        addMethodsFromClass(prefix, kind, CLASS, methods,
1137:                                seenSignatures, scannedClasses, true, true);
1138:                    }
1139:                } else {
1140:                    if ("ActiveRecord::Base".equals(extendsClass)) { // NOI18N
1141:                        // Add in database fields as well
1142:                        addDatabaseProperties(prefix, kind, classFqn, methods);
1143:                    }
1144:
1145:                    // We're not sure we have a fully qualified path, so try some different candidates
1146:                    if (!addMethodsFromClass(prefix, kind, extendsClass,
1147:                            methods, seenSignatures, scannedClasses,
1148:                            haveRedirected, true)) {
1149:                        // Search by classIn 
1150:                        String fqn = classIn;
1151:
1152:                        while (fqn != null) {
1153:                            if (addMethodsFromClass(prefix, kind, fqn + "::"
1154:                                    + extendsClass, methods, seenSignatures,
1155:                                    scannedClasses, haveRedirected, true)) {
1156:                                break;
1157:                            }
1158:
1159:                            int f = fqn.lastIndexOf("::"); // NOI18N
1160:
1161:                            if (f == -1) {
1162:                                break;
1163:                            } else {
1164:                                fqn = fqn.substring(0, f);
1165:                            }
1166:                        }
1167:                    }
1168:                }
1169:
1170:                return foundIt;
1171:            }
1172:
1173:            private void addDatabaseProperties(String prefix, NameKind kind,
1174:                    String classFqn, Set<IndexedMethod> methods) {
1175:                // Query index for database related properties
1176:                if (classFqn.indexOf("::") != -1) {
1177:                    // Don't know how to handle this scenario
1178:                    return;
1179:                }
1180:
1181:                String tableName = RubyUtils.tableize(classFqn);
1182:
1183:                String searchField = RubyIndexer.FIELD_DB_TABLE;
1184:                Set<SearchResult> result = new HashSet<SearchResult>();
1185:                search(searchField, tableName, NameKind.EXACT_NAME, result);
1186:
1187:                List<TableDefinition> tableDefs = new ArrayList<TableDefinition>();
1188:                TableDefinition schema = null;
1189:
1190:                for (SearchResult map : result) {
1191:                    assert map != null;
1192:
1193:                    String version = map.getValue(RubyIndexer.FIELD_DB_VERSION);
1194:                    assert tableName.equals(map
1195:                            .getValue(RubyIndexer.FIELD_DB_TABLE));
1196:                    String fileUrl = map.getPersistentUrl();
1197:
1198:                    TableDefinition def = new TableDefinition(tableName,
1199:                            version, fileUrl);
1200:                    tableDefs.add(def);
1201:                    String[] columns = map
1202:                            .getValues(RubyIndexer.FIELD_DB_COLUMN);
1203:
1204:                    if (columns != null) {
1205:                        for (String column : columns) {
1206:                            // TODO - do this filtering AFTER applying diffs when
1207:                            // I'm doing renaming of columns etc.
1208:                            def.addColumn(column);
1209:                        }
1210:                    }
1211:
1212:                    if (RubyIndexer.SCHEMA_INDEX_VERSION.equals(version)) {
1213:                        schema = def;
1214:                        // With a schema I don't need to look at anything else
1215:                        break;
1216:                    }
1217:                }
1218:
1219:                if (tableDefs.size() > 0) {
1220:                    Map<String, String> columnDefs = new HashMap<String, String>();
1221:                    Map<String, String> fileUrls = new HashMap<String, String>();
1222:                    Set<String> currentCols = new HashSet<String>();
1223:                    if (schema != null) {
1224:                        List<String> cols = schema.getColumns();
1225:                        if (cols != null) {
1226:                            for (String col : cols) {
1227:                                int typeIndex = col.indexOf(';');
1228:                                if (typeIndex != -1) {
1229:                                    String name = col.substring(0, typeIndex);
1230:                                    if (typeIndex < col.length() - 1
1231:                                            && col.charAt(typeIndex + 1) == '-') {
1232:                                        // Removing column - this is unlikely in a
1233:                                        // schema.rb file!
1234:                                        currentCols.remove(col);
1235:                                    } else {
1236:                                        currentCols.add(name);
1237:                                        fileUrls.put(col, schema.getFileUrl());
1238:                                        columnDefs.put(name, col);
1239:                                    }
1240:                                } else {
1241:                                    currentCols.add(col);
1242:                                    columnDefs.put(col, col);
1243:                                    fileUrls.put(col, schema.getFileUrl());
1244:                                }
1245:                            }
1246:                        }
1247:                    } else {
1248:                        // Apply migration files
1249:                        Collections.sort(tableDefs);
1250:                        for (TableDefinition def : tableDefs) {
1251:                            List<String> cols = def.getColumns();
1252:                            if (cols == null) {
1253:                                continue;
1254:                            }
1255:
1256:                            for (String col : cols) {
1257:                                int typeIndex = col.indexOf(';');
1258:                                if (typeIndex != -1) {
1259:                                    String name = col.substring(0, typeIndex);
1260:                                    if (typeIndex < col.length() - 1
1261:                                            && col.charAt(typeIndex + 1) == '-') {
1262:                                        // Removing column
1263:                                        currentCols.remove(name);
1264:                                    } else {
1265:                                        currentCols.add(name);
1266:                                        fileUrls.put(col, def.getFileUrl());
1267:                                        columnDefs.put(name, col);
1268:                                    }
1269:                                } else {
1270:                                    currentCols.add(col);
1271:                                    columnDefs.put(col, col);
1272:                                    fileUrls.put(col, def.getFileUrl());
1273:                                }
1274:                            }
1275:                        }
1276:                    }
1277:
1278:                    // Finally, we've "applied" the migrations - just walk
1279:                    // through the datastructure and create completion matches
1280:                    // as appropriate
1281:                    for (String column : currentCols) {
1282:                        if (column.startsWith(prefix)) {
1283:                            if (kind == NameKind.EXACT_NAME) {
1284:                                // Ensure that the method is not longer than the prefix
1285:                                if ((column.length() > prefix.length())) {
1286:                                    continue;
1287:                                }
1288:                            } else {
1289:                                // REGEXP, CAMELCASE filtering etc. not supported here
1290:                                assert (kind == NameKind.PREFIX)
1291:                                        || (kind == NameKind.CASE_INSENSITIVE_PREFIX);
1292:                            }
1293:
1294:                            String c = columnDefs.get(column);
1295:                            String type = tableName;
1296:                            int semicolonIndex = c.indexOf(';');
1297:                            if (semicolonIndex != -1) {
1298:                                type = c.substring(semicolonIndex + 1);
1299:                            }
1300:                            String fileUrl = fileUrls.get(column);
1301:
1302:                            String signature = column;
1303:                            String fqn = tableName + "#" + column;
1304:                            String clz = type;
1305:                            String require = null;
1306:                            String attributes = "";
1307:                            int flags = 0;
1308:
1309:                            IndexedMethod method = IndexedMethod.create(this ,
1310:                                    signature, fqn, clz, fileUrl, require,
1311:                                    attributes, flags);
1312:                            method
1313:                                    .setMethodType(IndexedMethod.MethodType.DBCOLUMN);
1314:                            method.setSmart(true);
1315:                            methods.add(method);
1316:                        }
1317:                    }
1318:
1319:                    if ("find_by_".startsWith(prefix)
1320:                            || "find_all_by".startsWith(prefix)) {
1321:                        // Generate dynamic finders
1322:                        for (String column : currentCols) {
1323:                            String methodOneName = "find_by_" + column;
1324:                            String methodAllName = "find_all_by_" + column;
1325:                            if (methodOneName.startsWith(prefix)
1326:                                    || methodAllName.startsWith(prefix)) {
1327:                                if (kind == NameKind.EXACT_NAME) {
1328:                                    // XXX methodOneName || methodAllName?                            
1329:                                    // Ensure that the method is not longer than the prefix
1330:                                    if ((column.length() > prefix.length())) {
1331:                                        continue;
1332:                                    }
1333:                                } else {
1334:                                    // REGEXP, CAMELCASE filtering etc. not supported here
1335:                                    assert (kind == NameKind.PREFIX)
1336:                                            || (kind == NameKind.CASE_INSENSITIVE_PREFIX);
1337:                                }
1338:
1339:                                String type = columnDefs.get(column);
1340:                                type = type.substring(type.indexOf(';') + 1);
1341:                                String fileUrl = fileUrls.get(column);
1342:
1343:                                String clz = classFqn;
1344:                                String require = null;
1345:                                int flags = IndexedElement.STATIC;
1346:                                String attributes = IndexedElement
1347:                                        .flagToString(flags)
1348:                                        + ";;;"
1349:                                        + "options(:first|:all),args(=>conditions|order|group|limit|offset|joins|readonly:bool|include|select|from|readonly:bool|lock:bool)";
1350:
1351:                                if (methodOneName.startsWith(prefix)) {
1352:                                    String signature = methodOneName + "("
1353:                                            + column + ",*options)";
1354:                                    String fqn = tableName + "#" + signature;
1355:                                    IndexedMethod method = IndexedMethod
1356:                                            .create(this , signature, fqn, clz,
1357:                                                    fileUrl, require,
1358:                                                    attributes, flags);
1359:                                    method.setInherited(false);
1360:                                    method.setSmart(true);
1361:                                    methods.add(method);
1362:                                }
1363:                                if (methodAllName.startsWith(prefix)) {
1364:                                    String signature = methodAllName + "("
1365:                                            + column + ",*options)";
1366:                                    String fqn = tableName + "#" + signature;
1367:                                    IndexedMethod method = IndexedMethod
1368:                                            .create(this , signature, fqn, clz,
1369:                                                    fileUrl, require,
1370:                                                    attributes, flags);
1371:                                    method.setInherited(false);
1372:                                    method.setSmart(true);
1373:                                    methods.add(method);
1374:                                }
1375:                            }
1376:                        }
1377:
1378:                    }
1379:                }
1380:            }
1381:
1382:            private class TableDefinition implements 
1383:                    Comparable<TableDefinition> {
1384:                private String version;
1385:                /** table is redundant, I only search by exact tablenames anyway */
1386:                private String table;
1387:                private String fileUrl;
1388:                private List<String> cols;
1389:
1390:                TableDefinition(String table, String version, String fileUrl) {
1391:                    this .table = table;
1392:                    this .version = version;
1393:                    this .fileUrl = fileUrl;
1394:                }
1395:
1396:                public int compareTo(RubyIndex.TableDefinition o) {
1397:                    // I can do string comparisons here because the strings
1398:                    // are all padded with zeroes on the left (so 100 is going
1399:                    // to be greater than 099, which wouldn't be true for "99".)
1400:                    return version.compareTo(o.version);
1401:                }
1402:
1403:                String getFileUrl() {
1404:                    return fileUrl;
1405:                }
1406:
1407:                void addColumn(String column) {
1408:                    if (cols == null) {
1409:                        cols = new ArrayList<String>();
1410:                    }
1411:
1412:                    cols.add(column);
1413:                }
1414:
1415:                List<String> getColumns() {
1416:                    return cols;
1417:                }
1418:            }
1419:
1420:            public Set<String> getDatabaseTables(String prefix, NameKind kind) {
1421:                // Query index for database related properties
1422:
1423:                String searchField = RubyIndexer.FIELD_DB_TABLE;
1424:                Set<SearchResult> result = new HashSet<SearchResult>();
1425:                search(searchField, prefix, kind, result);
1426:
1427:                Set<String> tables = new HashSet<String>();
1428:                for (SearchResult map : result) {
1429:                    assert map != null;
1430:
1431:                    String tableName = map.getValue(RubyIndexer.FIELD_DB_TABLE);
1432:                    if (tableName != null) {
1433:                        tables.add(tableName);
1434:                    }
1435:                }
1436:
1437:                return tables;
1438:            }
1439:
1440:            public Set<IndexedField> getInheritedFields(String classFqn,
1441:                    String prefix, NameKind kind, boolean inherited) {
1442:                boolean haveRedirected = false;
1443:
1444:                if ((classFqn == null) || classFqn.equals(OBJECT)) {
1445:                    // Redirect inheritance tree to Class to pick up methods in Class and Module
1446:                    classFqn = CLASS;
1447:                    haveRedirected = true;
1448:                } else if (MODULE.equals(classFqn) || CLASS.equals(classFqn)) {
1449:                    haveRedirected = true;
1450:                }
1451:
1452:                //String field = RubyIndexer.FIELD_FQN_NAME;
1453:                Set<IndexedField> members = new HashSet<IndexedField>();
1454:                Set<String> scannedClasses = new HashSet<String>();
1455:                Set<String> seenSignatures = new HashSet<String>();
1456:
1457:                boolean instanceVars = true;
1458:                if (prefix == null) {
1459:                    prefix = "";
1460:                } else if (prefix.startsWith("@@")) {
1461:                    instanceVars = false;
1462:                    prefix = prefix.substring(2);
1463:                } else if (prefix.startsWith("@")) {
1464:                    prefix = prefix.substring(1);
1465:                }
1466:
1467:                addFieldsFromClass(prefix, kind, classFqn, members,
1468:                        seenSignatures, scannedClasses, haveRedirected,
1469:                        instanceVars, inherited);
1470:
1471:                return members;
1472:            }
1473:
1474:            /** Return whether the specific class referenced (classFqn) was found or not. This is
1475:             * not the same as returning whether any classes were added since it may add
1476:             * additional methods from parents (Object/Class).
1477:             */
1478:            private boolean addFieldsFromClass(String prefix, NameKind kind,
1479:                    String classFqn, Set<IndexedField> methods,
1480:                    Set<String> seenSignatures, Set<String> scannedClasses,
1481:                    boolean haveRedirected, boolean instanceVars,
1482:                    boolean inheriting) {
1483:                // Prevent problems with circular includes or redundant includes
1484:                if (scannedClasses.contains(classFqn)) {
1485:                    return false;
1486:                }
1487:
1488:                scannedClasses.add(classFqn);
1489:
1490:                String searchField = RubyIndexer.FIELD_FQN_NAME;
1491:
1492:                Set<SearchResult> result = new HashSet<SearchResult>();
1493:
1494:                search(searchField, classFqn, NameKind.EXACT_NAME, result);
1495:
1496:                boolean foundIt = result.size() > 0;
1497:
1498:                // If this is a bogus class entry (no search rsults) don't continue
1499:                if (!foundIt) {
1500:                    return foundIt;
1501:                }
1502:
1503:                String extendsClass = null;
1504:
1505:                String classIn = null;
1506:                int fqnIndex = classFqn.lastIndexOf("::"); // NOI18N
1507:
1508:                if (fqnIndex != -1) {
1509:                    classIn = classFqn.substring(0, fqnIndex);
1510:                }
1511:
1512:                for (SearchResult map : result) {
1513:                    assert map != null;
1514:
1515:                    if (extendsClass == null) {
1516:                        extendsClass = map
1517:                                .getValue(RubyIndexer.FIELD_EXTENDS_NAME);
1518:                    }
1519:
1520:                    String includes = map.getValue(RubyIndexer.FIELD_INCLUDES);
1521:
1522:                    if (includes != null) {
1523:                        String[] in = includes.split(",");
1524:
1525:                        // I have Util::BacktraceFilter and Assertions, which are both
1526:                        // relative to ::,Test,Test::Unit
1527:                        for (String include : in) {
1528:                            // Try both with and without a package qualifier
1529:                            boolean isQualified = false;
1530:
1531:                            if (classIn != null) {
1532:                                isQualified = addFieldsFromClass(prefix, kind,
1533:                                        classIn + "::" + include, methods,
1534:                                        seenSignatures, scannedClasses,
1535:                                        haveRedirected, instanceVars, true);
1536:                            }
1537:
1538:                            if (!isQualified) {
1539:                                addFieldsFromClass(prefix, kind, include,
1540:                                        methods, seenSignatures,
1541:                                        scannedClasses, haveRedirected,
1542:                                        instanceVars, true);
1543:                            }
1544:                        }
1545:                    }
1546:
1547:                    String extendWith = map
1548:                            .getValue(RubyIndexer.FIELD_EXTEND_WITH);
1549:
1550:                    if (extendWith != null) {
1551:                        // Try both with and without a package qualifier
1552:                        boolean isQualified = false;
1553:
1554:                        if (classIn != null) {
1555:                            isQualified = addFieldsFromClass(prefix, kind,
1556:                                    classIn + "::" + extendWith, methods,
1557:                                    seenSignatures, scannedClasses,
1558:                                    haveRedirected, instanceVars, true);
1559:                        }
1560:
1561:                        if (!isQualified) {
1562:                            addFieldsFromClass(prefix, kind, extendWith,
1563:                                    methods, seenSignatures, scannedClasses,
1564:                                    haveRedirected, instanceVars, true);
1565:                        }
1566:                    }
1567:
1568:                    String[] fields = map
1569:                            .getValues(RubyIndexer.FIELD_FIELD_NAME);
1570:
1571:                    if (fields != null) {
1572:                        for (String field : fields) {
1573:                            // Skip weird methods like "[]" etc. in completion lists... TODO Think harder about this
1574:                            if ((prefix.length() == 0)
1575:                                    && !Character.isLowerCase(field.charAt(0))) {
1576:                                continue;
1577:                            }
1578:
1579:                            // Prevent duplicates when method is redefined
1580:                            if (!seenSignatures.contains(field)) {
1581:                                // See if we need instancevars or classvars
1582:                                boolean isInstance = true;
1583:                                int signatureIndex = field.indexOf(';');
1584:                                if (signatureIndex != -1
1585:                                        && field.indexOf('s',
1586:                                                signatureIndex + 1) != -1) {
1587:                                    isInstance = false;
1588:                                }
1589:                                if (isInstance != instanceVars) {
1590:                                    continue;
1591:                                }
1592:
1593:                                if (field.startsWith(prefix)) {
1594:                                    if (kind == NameKind.EXACT_NAME) {
1595:                                        // Ensure that the method is not longer than the prefix
1596:                                        if ((field.length() > prefix.length())
1597:                                                && (field.charAt(prefix
1598:                                                        .length()) != '(')
1599:                                                && (field.charAt(prefix
1600:                                                        .length()) != ';')) {
1601:                                            continue;
1602:                                        }
1603:                                    } else {
1604:                                        // REGEXP, CAMELCASE filtering etc. not supported here
1605:                                        assert (kind == NameKind.PREFIX)
1606:                                                || (kind == NameKind.CASE_INSENSITIVE_PREFIX);
1607:                                    }
1608:
1609:                                    seenSignatures.add(field);
1610:
1611:                                    IndexedField f = createField(field, map,
1612:                                            isInstance, inheriting);
1613:                                    f.setSmart(!haveRedirected);
1614:                                    methods.add(f);
1615:                                }
1616:                            }
1617:                        }
1618:                    }
1619:                }
1620:
1621:                if (classFqn.equals(OBJECT)) {
1622:                    return foundIt;
1623:                }
1624:
1625:                if (extendsClass == null) {
1626:                    if (haveRedirected) {
1627:                        addFieldsFromClass(prefix, kind, OBJECT, methods,
1628:                                seenSignatures, scannedClasses, true,
1629:                                instanceVars, true);
1630:                    } else {
1631:                        // Rather than inheriting directly from object,
1632:                        // let's go via Class (and Module) up to Object
1633:                        addFieldsFromClass(prefix, kind, CLASS, methods,
1634:                                seenSignatures, scannedClasses, true,
1635:                                instanceVars, true);
1636:                    }
1637:                } else {
1638:                    // We're not sure we have a fully qualified path, so try some different candidates
1639:                    if (!addFieldsFromClass(prefix, kind, extendsClass,
1640:                            methods, seenSignatures, scannedClasses,
1641:                            haveRedirected, instanceVars, true)) {
1642:                        // Search by classIn 
1643:                        String fqn = classIn;
1644:
1645:                        while (fqn != null) {
1646:                            if (addFieldsFromClass(prefix, kind, fqn + "::"
1647:                                    + extendsClass, methods, seenSignatures,
1648:                                    scannedClasses, haveRedirected,
1649:                                    instanceVars, true)) {
1650:                                break;
1651:                            }
1652:
1653:                            int f = fqn.lastIndexOf("::"); // NOI18N
1654:
1655:                            if (f == -1) {
1656:                                break;
1657:                            } else {
1658:                                fqn = fqn.substring(0, f);
1659:                            }
1660:                        }
1661:                    }
1662:                }
1663:
1664:                return foundIt;
1665:            }
1666:
1667:            /** Return all the method or class definitions for the given FQN that are documented. */
1668:            public Set<? extends IndexedElement> getDocumented(final String fqn) {
1669:                assert (fqn != null) && (fqn.length() > 0);
1670:
1671:                int hashIndex = fqn.indexOf('#');
1672:
1673:                if (hashIndex == -1) {
1674:                    // Looking for a class or a module
1675:                    return getDocumentedClasses(fqn);
1676:                } else {
1677:                    // Looking for a method
1678:                    String clz = fqn.substring(0, hashIndex);
1679:                    String method = fqn.substring(hashIndex + 1);
1680:
1681:                    return getDocumentedMethods(clz, method);
1682:                }
1683:            }
1684:
1685:            private Set<IndexedClass> getDocumentedClasses(final String fqn) {
1686:                final Set<SearchResult> result = new HashSet<SearchResult>();
1687:                String field = RubyIndexer.FIELD_FQN_NAME;
1688:
1689:                search(field, fqn, NameKind.EXACT_NAME, result);
1690:
1691:                Set<IndexedClass> matches = new HashSet<IndexedClass>();
1692:
1693:                for (SearchResult map : result) {
1694:                    assert map != null;
1695:
1696:                    String attributes = map
1697:                            .getValue(RubyIndexer.FIELD_CLASS_ATTRS);
1698:
1699:                    if (attributes != null) {
1700:                        int flags = IndexedElement.stringToFlag(attributes, 0);
1701:                        if ((flags & IndexedElement.DOCUMENTED) != 0) {
1702:                            matches.add(createClass(fqn, null, map));
1703:                        }
1704:                    }
1705:                }
1706:
1707:                return matches;
1708:            }
1709:
1710:            private Set<IndexedMethod> getDocumentedMethods(final String fqn,
1711:                    String method) {
1712:                final Set<SearchResult> result = new HashSet<SearchResult>();
1713:                String field = RubyIndexer.FIELD_FQN_NAME;
1714:
1715:                search(field, fqn, NameKind.EXACT_NAME, result);
1716:
1717:                Set<IndexedMethod> matches = new HashSet<IndexedMethod>();
1718:
1719:                for (SearchResult map : result) {
1720:                    String[] signatures = map
1721:                            .getValues(RubyIndexer.FIELD_METHOD_NAME);
1722:
1723:                    if (signatures != null) {
1724:                        for (String signature : signatures) {
1725:                            // Skip weird methods... Think harder about this
1726:                            if (((method == null) || (method.length() == 0))
1727:                                    && !Character.isLowerCase(signature
1728:                                            .charAt(0))) {
1729:                                continue;
1730:                            }
1731:
1732:                            if (!signature.startsWith(method)) {
1733:                                continue;
1734:                            }
1735:
1736:                            // Make sure the name matches exactly
1737:                            // We know that the prefix is correct from the first part of
1738:                            // this if clause, by the signature may have more
1739:                            if (((signature.length() > method.length()) && (signature
1740:                                    .charAt(method.length()) != '('))
1741:                                    && (signature.charAt(method.length()) != ';')) {
1742:                                continue;
1743:                            }
1744:
1745:                            int attributes = signature.indexOf(';', method
1746:                                    .length());
1747:                            if (attributes == -1) {
1748:                                continue;
1749:                            }
1750:                            int flags = IndexedElement.stringToFlag(signature,
1751:                                    attributes + 1);
1752:                            if ((flags & IndexedElement.DOCUMENTED) != 0) {
1753:                                // Method is documented
1754:                                assert map != null;
1755:                                matches
1756:                                        .add(createMethod(signature, map, false));
1757:                            }
1758:                        }
1759:                    }
1760:
1761:                    String[] attribs = map
1762:                            .getValues(RubyIndexer.FIELD_ATTRIBUTE_NAME);
1763:
1764:                    if (attribs != null) {
1765:                        for (String signature : attribs) {
1766:                            // Skip weird methods... Think harder about this
1767:                            if (((method == null) || (method.length() == 0))
1768:                                    && !Character.isLowerCase(signature
1769:                                            .charAt(0))) {
1770:                                continue;
1771:                            }
1772:
1773:                            if (!signature.startsWith(method)) {
1774:                                continue;
1775:                            }
1776:
1777:                            // Make sure the name matches exactly
1778:                            // We know that the prefix is correct from the first part of
1779:                            // this if clause, by the signature may have more
1780:                            if (((signature.length() > method.length()) &&
1781:                            //(signature.charAt(method.length()) != '(')) &&
1782:                            (signature.charAt(method.length()) != ';'))) {
1783:                                continue;
1784:                            }
1785:
1786:                            // TODO - index whether attributes are documented!
1787:                            //int attributes = signature.indexOf(';', method.length());
1788:                            //
1789:                            //if (attributes == -1) {
1790:                            //    continue;
1791:                            //}
1792:                            //
1793:                            //if (signature.indexOf('d', attributes + 1) != -1) {
1794:                            //    // Method is documented
1795:                            assert map != null;
1796:                            matches.add(createMethod(signature, map, false));
1797:                            //}
1798:                        }
1799:                    }
1800:                }
1801:
1802:                return matches;
1803:            }
1804:
1805:            /** Return the file url corresponding to the given require statement */
1806:            public String getRequiredFileUrl(final String require) {
1807:                final Set<SearchResult> result = new HashSet<SearchResult>();
1808:
1809:                String field = RubyIndexer.FIELD_REQUIRE;
1810:
1811:                search(field, require, NameKind.EXACT_NAME, result);
1812:
1813:                // TODO Prune methods to fit my scheme - later make lucene index smarter about how to prune its index search
1814:                for (SearchResult map : result) {
1815:                    String file = map.getPersistentUrl();
1816:
1817:                    if (file != null) {
1818:                        return file;
1819:                    }
1820:                }
1821:
1822:                return null;
1823:            }
1824:
1825:            static String getClusterUrl() {
1826:                if (clusterUrl == null) {
1827:                    File f = InstalledFileLocator.getDefault().locate(
1828:                            "modules/org-netbeans-modules-ruby.jar", null,
1829:                            false); // NOI18N
1830:
1831:                    if (f == null) {
1832:                        throw new RuntimeException("Can't find cluster");
1833:                    }
1834:
1835:                    f = new File(f.getParentFile().getParentFile()
1836:                            .getAbsolutePath());
1837:
1838:                    try {
1839:                        f = f.getCanonicalFile();
1840:                        clusterUrl = f.toURI().toURL().toExternalForm();
1841:                    } catch (IOException ioe) {
1842:                        Exceptions.printStackTrace(ioe);
1843:                    }
1844:                }
1845:
1846:                return clusterUrl;
1847:            }
1848:
1849:            // For testing only
1850:            static void setClusterUrl(String url) {
1851:                clusterUrl = url;
1852:            }
1853:
1854:            static String getPreindexUrl(String url) {
1855:                if (RubyIndexer.PREINDEXING) {
1856:                    Iterator<RubyPlatform> it = RubyPlatformManager
1857:                            .platformIterator();
1858:                    while (it.hasNext()) {
1859:                        RubyPlatform platform = it.next();
1860:                        String s = getGemHomeURL(platform);
1861:
1862:                        if (s != null && url.startsWith(s)) {
1863:                            return GEM_URL + url.substring(s.length());
1864:                        }
1865:
1866:                        s = platform.getHomeUrl();
1867:
1868:                        if (url.startsWith(s)) {
1869:                            url = RUBYHOME_URL + url.substring(s.length());
1870:
1871:                            return url;
1872:                        }
1873:                    }
1874:                } else {
1875:                    // FIXME: use right platform
1876:                    RubyPlatform platform = RubyPlatformManager
1877:                            .getDefaultPlatform();
1878:                    String s = getGemHomeURL(platform);
1879:
1880:                    if (s != null && url.startsWith(s)) {
1881:                        return GEM_URL + url.substring(s.length());
1882:                    }
1883:
1884:                    s = platform.getHomeUrl();
1885:
1886:                    if (url.startsWith(s)) {
1887:                        url = RUBYHOME_URL + url.substring(s.length());
1888:
1889:                        return url;
1890:                    }
1891:                }
1892:
1893:                String s = getClusterUrl();
1894:
1895:                if (url.startsWith(s)) {
1896:                    return CLUSTER_URL + url.substring(s.length());
1897:                }
1898:
1899:                return url;
1900:            }
1901:
1902:            /** Get the FileObject corresponding to a URL returned from the index */
1903:            public static FileObject getFileObject(String url) {
1904:                try {
1905:                    if (url.startsWith(RUBYHOME_URL)) {
1906:                        // TODO - resolve to correct platform
1907:                        // FIXME: per-platform now
1908:                        Iterator<RubyPlatform> it = RubyPlatformManager
1909:                                .platformIterator();
1910:                        while (it.hasNext()) {
1911:                            RubyPlatform platform = it.next();
1912:                            url = platform.getHomeUrl()
1913:                                    + url.substring(RUBYHOME_URL.length());
1914:                            FileObject fo = URLMapper.findFileObject(new URL(
1915:                                    url));
1916:                            if (fo != null) {
1917:                                return fo;
1918:                            }
1919:                        }
1920:
1921:                        return null;
1922:                    } else if (url.startsWith(GEM_URL)) {
1923:                        // FIXME: per-platform now
1924:                        Iterator<RubyPlatform> it = RubyPlatformManager
1925:                                .platformIterator();
1926:                        while (it.hasNext()) {
1927:                            RubyPlatform platform = it.next();
1928:                            if (!platform.hasRubyGemsInstalled()) {
1929:                                continue;
1930:                            }
1931:                            url = platform.getGemManager().getGemHomeUrl()
1932:                                    + url.substring(GEM_URL.length());
1933:                            FileObject fo = URLMapper.findFileObject(new URL(
1934:                                    url));
1935:                            if (fo != null) {
1936:                                return fo;
1937:                            }
1938:                        }
1939:
1940:                        return null;
1941:                    } else if (url.startsWith(CLUSTER_URL)) {
1942:                        url = getClusterUrl()
1943:                                + url.substring(CLUSTER_URL.length()); // NOI18N
1944:                    }
1945:
1946:                    return URLMapper.findFileObject(new URL(url));
1947:                } catch (MalformedURLException mue) {
1948:                    Exceptions.printStackTrace(mue);
1949:                }
1950:
1951:                return null;
1952:            }
1953:
1954:            private static String getGemHomeURL(RubyPlatform platform) {
1955:                return platform.hasRubyGemsInstalled() ? platform
1956:                        .getGemManager().getGemHomeUrl() : null;
1957:            }
1958:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.