PageRenderTime 34ms CodeModel.GetById 15ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/cache/RubyCache.java

#
Java | 301 lines | 235 code | 44 blank | 22 comment | 31 complexity | ac9b32ae107eff3b0901831bb21ec2cd MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1/*
  2 * RubyCache.java - Cache of Ruby methods, classes, modules
  3 *
  4 * Copyright 2005 Robert McKinnon
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version 2
  9 * of the License, or any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful,
 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 * GNU General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU General Public License
 17 * along with this program; if not, write to the Free Software
 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 19 */
 20package org.jedit.ruby.cache;
 21
 22import org.jedit.ruby.ast.*;
 23import org.jedit.ruby.parser.RubyParser;
 24import org.jedit.ruby.RubyPlugin;
 25
 26import java.util.*;
 27
 28/**
 29 * @author robmckinnon at users.sourceforge.net
 30 */
 31public final class RubyCache {
 32
 33    private static RubyCache instance;
 34
 35    private final NameToMethods nameToMethods;
 36    private final NameToParents nameToParents;
 37    private final MethodToParents methodToParents;
 38    private final ParentToMethods parentToMethods;
 39    private final ParentToImmediateMethods parentToImmediateMethods;
 40    private final Map<String, RubyMembers> pathToMembers;
 41
 42    public static synchronized void resetCache() {
 43        instance = new RubyCache();
 44    }
 45
 46    public static synchronized RubyCache instance() {
 47        return instance;
 48    }
 49
 50    private RubyCache() {
 51        nameToMethods = new NameToMethods();
 52        nameToParents = new NameToParents();
 53        methodToParents = new MethodToParents();
 54        parentToMethods = new ParentToMethods();
 55        parentToImmediateMethods = new ParentToImmediateMethods();
 56        pathToMembers = new HashMap<String, RubyMembers>();
 57        nameToParents.setParentToImmediateMethods(parentToImmediateMethods);
 58        parentToMethods.setNameToParents(nameToParents);
 59    }
 60
 61    public final synchronized void addMembers(String text, String path) {
 62        RubyMembers members = RubyParser.getMembers(text, path, null, true);
 63        addMembers(members, path);
 64    }
 65
 66    public final synchronized void addMembers(RubyMembers members, String path) {
 67        if (!members.containsErrors()) {
 68            add(members, path);
 69        }
 70    }
 71
 72    public final synchronized void addClass(ClassMember parent, String path) {
 73        Member[] members = new Member[1];
 74        members[0] = parent;
 75        RubyMembers rubyMembers = new RubyMembers(members, new ArrayList<Problem>(), 0);
 76        RubyCache.instance().addMembers(rubyMembers, path);
 77    }
 78
 79    public final synchronized ClassMember getClass(String className) {
 80        return nameToParents.getClass(className);
 81    }
 82
 83    public final synchronized ParentMember getParentMember(String parentMemberName) {
 84        return nameToParents.getMember(parentMemberName);
 85    }
 86
 87    public final synchronized List<Method> getMethods(String method) {
 88        return new ArrayList<Method>(nameToMethods.getMethods(method));
 89    }
 90
 91    public final synchronized Set<Member> getMembersWithMethod(String method) {
 92        return new HashSet<Member>(methodToParents.getParentSet(method));
 93    }
 94
 95    public final synchronized Set<Method> getMethodsOfMember(String memberName) {
 96        return new HashSet<Method>(parentToMethods.getMethodSet(memberName));
 97    }
 98
 99    public final synchronized Set<Method> getAllMethods() {
100        return parentToMethods.getAllMethods();
101    }
102
103    public final synchronized List<Member> getAllImmediateMembers() {
104        return new ArrayList<Member>(nameToParents.getAllMembers());
105    }
106
107    public final synchronized List<Member> getMembersWithMethodAsList(String method) {
108        return new ArrayList<Member>(methodToParents.getParentList(method));
109    }
110
111    public final synchronized List<Method> getMethodsOfMemberAsList(String memberName) {
112        return new ArrayList<Method>(parentToMethods.getMethodList(memberName));
113    }
114
115    public final synchronized void populateSuperClassMethods() {
116        Collection<ParentMember> allParents = nameToParents.getAllParents();
117
118        for (ParentMember member : allParents) {
119            member.accept(new MemberVisitorAdapter() {
120                public void handleClass(ClassMember classMember) {
121                    populateSuperClassMethods(classMember, classMember);
122                }
123            });
124        }
125
126        if (nameToParents.getMember("ActionController::Base") != null) {
127            ClassMember appController = new ClassMember("ApplicationController");
128            appController.setSuperClassName("ActionController::Base");
129            appController.setEndOffset(0);
130            appController.setDocumentationComment(
131                    "<p>ApplicationController is the parent of all the controller classes in a Rails application." +
132                    " By default in app/controllers there is a file called application.rb that contains an empty" +
133                    " definition of class ApplicationController.</p>" +
134                    "<p>This class is used to establish a context for the entire application." +
135                    " Filters added to this controller will be run for all controllers in the application." +
136                    " Likewise, all the methods added will be available for all controllers.</p>");
137
138            addClass(appController, "ApplicationController");
139            populateSuperClassMethods(appController, appController);
140        }
141
142        Set<Method> methods = getAllMethods();
143
144        for (Method method : methods) {
145            method.populateReturnTypes();
146        }
147    }
148
149    private void add(RubyMembers members, String path) {
150        parentToMethods.resetAllMethodsList();
151        pathToMembers.put(path, members);
152        members.visitMembers(new MemberVisitorAdapter() {
153            public void handleModule(Module module) {
154                parentToImmediateMethods.add(module);
155                parentToMethods.add(module);
156                nameToParents.add(module);
157            }
158
159            public void handleClass(ClassMember classMember) {
160                parentToImmediateMethods.add(classMember);
161                parentToMethods.add(classMember);
162                nameToParents.add(classMember);
163            }
164
165            public void handleMethod(Method method) {
166                methodToParents.add(method);
167                nameToMethods.add(method);
168            }
169        });
170    }
171
172    private final static String[] ACTION_CONTROLLER_BASE_INCLUDES = new String[]{
173            "ActionController::Filters",
174            "ActionController::Layout",
175            "ActionController::Flash",
176            "ActionController::Benchmarking",
177            "ActionController::Rescue",
178            "ActionController::Dependencies",
179            "ActionController::Pagination",
180            "ActionController::Scaffolding",
181            "ActionController::Helpers",
182            "ActionController::Cookies",
183            "ActionController::Caching",
184            "ActionController::Components",
185            "ActionController::Verification",
186            "ActionController::Streaming",
187            "ActionController::SessionManagement",
188            "ActionController::Macros::AutoComplete",
189            "ActionController::Macros::InPlaceEditing"
190    };
191
192    private final static String[] ACTIVE_RECORD_BASE_INCLUDES = new String[]{
193            "ActiveRecord::Validations",
194            "ActiveRecord::Locking",
195            "ActiveRecord::Callbacks",
196            "ActiveRecord::Observing",
197            "ActiveRecord::Timestamp",
198            "ActiveRecord::Associations",
199            "ActiveRecord::Aggregations",
200            "ActiveRecord::Transactions",
201            "ActiveRecord::Reflection",
202            "ActiveRecord::Acts::Tree",
203            "ActiveRecord::Acts::List",
204            "ActiveRecord::Acts::NestedSet"
205    };
206
207    private void populateSuperClassMethods(ClassMember member, ClassMember memberOrSuperclass) {
208        if (memberOrSuperclass.hasSuperClassName()) {
209            String superClassName = memberOrSuperclass.getSuperClassName();
210            ClassMember superClass = nameToParents.getClass(superClassName);
211            if (superClass != null) {
212                addSuperClassMethods(member, superClass.getMethods());
213                populateSuperClassMethods(member, superClass);
214            }
215
216            addIncludes(member, "ActiveRecord::Base", ACTIVE_RECORD_BASE_INCLUDES);
217            addIncludes(member, "ActionController::Base", ACTION_CONTROLLER_BASE_INCLUDES);
218        }
219    }
220
221    private void addIncludes(ClassMember member, String fullName, String[] includeModules) {
222        if (member.getFullName().equals(fullName)) {
223            for (String include : includeModules) {
224                ClassMember classMethodClass = nameToParents.getClass(include + "::ClassMethods");
225                if (classMethodClass != null && classMethodClass.getMethods().size() > 0) {
226                    includeClassMethods(member, classMethodClass);
227                } else {
228                    classMethodClass = nameToParents.getClass(include);
229                    if (classMethodClass != null) {
230                        includeClassMethods(member, classMethodClass);
231                    }
232                }
233            }
234        }
235    }
236
237    private void includeClassMethods(ClassMember member, ClassMember classMethodClass) {
238        Set<Method> methods = classMethodClass.getMethods();
239        Set<Method> classMethods = new HashSet<Method>();
240
241        for (Method classMethod : methods) {
242            classMethod.setClassMethod(true);
243            classMethods.add(classMethod);
244        }
245        addSuperClassMethods(member, classMethods);
246    }
247
248    private void addSuperClassMethods(ClassMember member, Set<Method> superClassMethods) {
249        for (Method method : superClassMethods) {
250            methodToParents.add(method, member);
251        }
252        superClassMethods = filterOutDuplicates(member, superClassMethods);
253        parentToMethods.add(member, superClassMethods);
254    }
255
256    private Set<Method> filterOutDuplicates(ParentMember member, Set<Method> parentMethods) {
257        Set<String> methodNames = getMethodNames(member);
258        Iterator<Method> parentMethodIterator = parentMethods.iterator();
259
260        while (parentMethodIterator.hasNext()) {
261            Method parentMethod = parentMethodIterator.next();
262            if (methodNames.contains(parentMethod.getName())) {
263                parentMethodIterator.remove();
264            }
265        }
266        return parentMethods;
267    }
268
269    private Set<String> getMethodNames(ParentMember member) {
270        Set<Method> methods = parentToMethods.getMethodSet(member.getName());
271        Set<String> methodNames = new HashSet<String>();
272        for (Method method : methods) {
273            methodNames.add(method.getName());
274        }
275        return methodNames;
276    }
277
278    public List<ParentMember> getParentsStartingWith(String partialClass, boolean ignoreCase) {
279        List<ParentMember> members = new ArrayList<ParentMember>();
280        List<String> names = nameToParents.getAllParentNames();
281
282        String lowerCasePartial = null;
283        if (ignoreCase && partialClass != null) {
284            lowerCasePartial = partialClass.toLowerCase();
285        }
286
287        for (String name : names) {
288            if (name.startsWith(partialClass)) {
289                ParentMember member = nameToParents.getMember(name);
290                members.add(member);
291            } else if (ignoreCase && lowerCasePartial != null) {
292                if (name.toLowerCase().startsWith(lowerCasePartial)) {
293                    ParentMember member = nameToParents.getMember(name);
294                    members.add(member);
295                }
296            }
297        }
298
299        return members;
300    }
301}