/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}