/* * Author: C.Williams * * Copyright (c) 2004 RubyPeople. * * This file is part of the Ruby Development Tools (RDT) plugin for eclipse. You * can get copy of the GPL along with further information about RubyPeople and * third party software bundled with RDT in the file * org.rubypeople.rdt.core_x.x.x/RDT.license or otherwise at * http://www.rubypeople.org/RDT.license. * * RDT is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * RDT is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * RDT; if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA */ package org.rubypeople.rdt.internal.core; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.rubypeople.rdt.core.IField; import org.rubypeople.rdt.core.IMember; import org.rubypeople.rdt.core.IMethod; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.ISourceFolder; import org.rubypeople.rdt.core.ISourceFolderRoot; import org.rubypeople.rdt.core.IType; import org.rubypeople.rdt.core.ITypeHierarchy; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.core.WorkingCopyOwner; import org.rubypeople.rdt.core.search.SearchEngine; import org.rubypeople.rdt.internal.core.util.MementoTokenizer; /** * @author Chris * */ public class RubyType extends NamedMember implements IType { /** * The list of core classes whose parent is not Object or include modules, but could be re-defined without specifying their superclass or the included modules. */ private static final String[] CORE_NAMES = new String[] { "Array", "BasicSocket", "Bignum", "Class", "Complex", "Continuation", "Data", "Date", "DateTime", "Delegator", "Dir", "Enumerable", "Error", "FalseClass", "File", "Fixnum", "Float", "Generator", "Hash", "IO", "IPSocket", "Iconv", "Integer", "Interrupt", "InvalidArgument", "List", "LoadError", "LocalJumpError", "Monitor", "NameError", "NilClass", "Numeric", "ParseError", "Prime", "Range", "RangeError", "Rational", "RegAnd", "RegOr", "Regexp", "RegexpError", "ScriptError", "SecurityError", "Set", "Shell", "SignalException", "SimpleDelegator", "SizedQueue", "Socket", "SocketError", "SortedSet", "StandardError", "String", "StringIO", "Struct", "Symbol", "SyncEnumerator", "SyntaxError", "SystemCallError", "SystemExit", " SystemStackError", "TCPServer", "TCPSocket", "TempFile", "Thread", "ThreadError", "Time", "Timeout", "Tracer", "TrueClass", "TruncatedDataError", "TypeError", "UDPSocket", "URI", "UnboundMethod", "Vector", "ZeroDivisionError"}; public RubyType(RubyElement parent, String name) { super(parent, name); } /** * @see IType */ public String getSuperclassName() throws RubyModelException { if (isCoreClass() && !isCoreStub()) { IType type = getCoreClass(getElementName()); if (type != null) return type.getSuperclassName(); } RubyTypeElementInfo info = (RubyTypeElementInfo) getElementInfo(); return info.getSuperclassName(); } private boolean isCoreStub() throws RubyModelException { ISourceFolderRoot root = (ISourceFolderRoot) getAncestor(IRubyElement.SOURCE_FOLDER_ROOT); return root.equals(getCoreStubRoot()); } private IType getCoreClass(String elementName) throws RubyModelException { ISourceFolderRoot root = getCoreStubRoot(); if (root == null) return null; ISourceFolder folder = root.getSourceFolder(new String[0]); IRubyScript script = folder.getRubyScript(elementName.toLowerCase() + ".rb"); return script.getType(elementName); } private ISourceFolderRoot getCoreStubRoot() throws RubyModelException { ISourceFolderRoot[] roots = getRubyProject().getSourceFolderRoots(); for (int i = 0; i < roots.length; i++) { IPath path = roots[i].getPath(); String string = path.toPortableString(); if (string.contains("org.rubypeople.rdt.launching")) { return roots[i]; } } return null; } private boolean isCoreClass() { String name = getElementName(); for (int i = 0; i < CORE_NAMES.length; i++) { if (CORE_NAMES[i].equals(name)) return true; } return false; } /** * @see IType */ public String[] getIncludedModuleNames() throws RubyModelException { RubyTypeElementInfo info = (RubyTypeElementInfo) getElementInfo(); String[] modules = info.getIncludedModuleNames(); if ((modules == null || modules.length == 0) && getFullyQualifiedName().equals("Object")) { return new String[] {"Kernel"}; } return modules; } /** * @see IType#isMember() */ public boolean isMember() { return getDeclaringType() != null; } /* * (non-Javadoc) * * @see org.rubypeople.rdt.internal.core.parser.RubyElement#getElementType() */ public int getElementType() { return IRubyElement.TYPE; } /** * @see IType#getField */ public IField getField(String fieldName) { if (fieldName.startsWith("@@")) return new RubyClassVar(this, fieldName); if (fieldName.startsWith("@")) return new RubyInstVar(this, fieldName); if (fieldName.startsWith("$")) return new RubyGlobal(this, fieldName); if (Character.isUpperCase(fieldName.charAt(0))) return new RubyConstant(this, fieldName); Assert.isTrue(false, "Tried to access a field which isn't an instance variable, class variable, global or constant"); return null; } /** * @see IType */ public IField[] getFields() throws RubyModelException { ArrayList list = getChildrenOfType(CONSTANT); list.addAll(getChildrenOfType(INSTANCE_VAR)); list.addAll(getChildrenOfType(CLASS_VAR)); IField[] array = new IField[list.size()]; list.toArray(array); return array; } public IMethod getMethod(String name, String[] parameterNames) { return new RubyMethod(this, name, parameterNames); } /** * @see IType */ public IMethod[] getMethods() throws RubyModelException { ArrayList list = getChildrenOfType(METHOD); IMethod[] array = new IMethod[list.size()]; list.toArray(array); return array; } /** * @see IMember */ public IType getDeclaringType() { IRubyElement parentElement = getParent(); while (parentElement != null) { if (parentElement.getElementType() == IRubyElement.TYPE) { return (IType) parentElement; } else if (parentElement instanceof IMember) { parentElement = parentElement.getParent(); } else { return null; } } return null; } /* * @see RubyElement#getPrimaryElement(boolean) */ public IRubyElement getPrimaryElement(boolean checkOwner) { if (checkOwner) { RubyScript cu = (RubyScript) getAncestor(SCRIPT); if (cu.isPrimary()) return this; } IRubyElement primaryParent = this.parent.getPrimaryElement(false); switch (primaryParent.getElementType()) { case IRubyElement.SCRIPT: return ((IRubyScript) primaryParent).getType(this.name); case IRubyElement.TYPE: return ((IType) primaryParent).getType(this.name); case IRubyElement.INSTANCE_VAR: case IRubyElement.CLASS_VAR: case IRubyElement.BLOCK: case IRubyElement.LOCAL_VARIABLE: case IRubyElement.METHOD: return ((IMember) primaryParent).getType(this.name, this.occurrenceCount); } return this; } /** * @see IType */ public IType getType(String typeName) { return new RubyType(this, typeName); } public boolean equals(Object o) { if (!(o instanceof RubyType)) return false; return super.equals(o); } /* * (non-Javadoc) * * @see org.rubypeople.rdt.core.IRubyType#isClass() */ public boolean isClass() { return true; } /* * (non-Javadoc) * * @see org.rubypeople.rdt.core.IRubyType#isModule() */ public boolean isModule() { return false; } public IMethod createMethod(String contents, IRubyElement sibling, boolean force, IProgressMonitor monitor) throws RubyModelException { CreateMethodOperation op = new CreateMethodOperation(this, contents, force); if (sibling != null) { op.createBefore(sibling); } op.runOperation(monitor); return (IMethod) op.getResultElements()[0]; } public ISourceFolder getSourceFolder() { IRubyElement parentElement = this.parent; while (parentElement != null) { if (parentElement.getElementType() == IRubyElement.SOURCE_FOLDER) { return (ISourceFolder)parentElement; } else { parentElement = parentElement.getParent(); } } Assert.isTrue(false); // should not happen return null; } public String getFullyQualifiedName() { IType declaring = getDeclaringType(); if (declaring != null) { return declaring.getFullyQualifiedName() + "::" + getElementName(); } return getElementName(); } /* * @see RubyElement */ public IRubyElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner workingCopyOwner) { switch (token.charAt(0)) { case JEM_COUNT: return getHandleUpdatingCountFromMemento(memento, workingCopyOwner); case JEM_FIELD: if (!memento.hasMoreTokens()) return this; String fieldName = memento.nextToken(); RubyElement field = (RubyElement)getField(fieldName); return field.getHandleFromMemento(memento, workingCopyOwner); case JEM_METHOD: if (!memento.hasMoreTokens()) return this; String selector = memento.nextToken(); ArrayList params = new ArrayList(); nextParam: while (memento.hasMoreTokens()) { token = memento.nextToken(); switch (token.charAt(0)) { case JEM_TYPE: break nextParam; case JEM_METHOD: if (!memento.hasMoreTokens()) return this; String param = memento.nextToken(); StringBuffer buffer = new StringBuffer(); params.add(buffer.toString() + param); break; default: break nextParam; } } String[] parameters = new String[params.size()]; params.toArray(parameters); RubyElement method = (RubyElement)getMethod(selector, parameters); switch (token.charAt(0)) { case JEM_TYPE: case JEM_LOCALVARIABLE: return method.getHandleFromMemento(token, memento, workingCopyOwner); default: return method; } case JEM_TYPE: String typeName; if (memento.hasMoreTokens()) { typeName = memento.nextToken(); char firstChar = typeName.charAt(0); if (firstChar == JEM_FIELD || firstChar == JEM_METHOD || firstChar == JEM_TYPE || firstChar == JEM_COUNT) { token = typeName; typeName = ""; //$NON-NLS-1$ } else { token = null; } } else { typeName = ""; //$NON-NLS-1$ token = null; } RubyElement type = (RubyElement)getType(typeName); if (token == null) { return type.getHandleFromMemento(memento, workingCopyOwner); } else { return type.getHandleFromMemento(token, memento, workingCopyOwner); } } return null; } /** * @see IType#getTypeQualifiedName(char) */ public String getTypeQualifiedName(String enclosingTypeSeparator) { try { return getTypeQualifiedName(enclosingTypeSeparator, false/*don't show parameters*/); } catch (RubyModelException e) { // exception thrown only when showing parameters return null; } } /** * @see IType */ public IType[] getTypes() throws RubyModelException { ArrayList list= getChildrenOfType(TYPE); IType[] array= new IType[list.size()]; list.toArray(array); return array; } /** * @see IType */ public ITypeHierarchy newTypeHierarchy(IProgressMonitor monitor) throws RubyModelException { CreateTypeHierarchyOperation op= new CreateTypeHierarchyOperation(this, null, SearchEngine.createWorkspaceScope(), true); op.runOperation(monitor); return op.getResult(); } /** * @see IType#newTypeHierarchy(WorkingCopyOwner, IProgressMonitor) */ public ITypeHierarchy newTypeHierarchy( WorkingCopyOwner owner, IProgressMonitor monitor) throws RubyModelException { IRubyScript[] workingCopies = RubyModelManager.getRubyModelManager().getWorkingCopies(owner, true/*add primary working copies*/); CreateTypeHierarchyOperation op= new CreateTypeHierarchyOperation(this, workingCopies, SearchEngine.createWorkspaceScope(), true); op.runOperation(monitor); return op.getResult(); } /** * @see IType */ public ITypeHierarchy newSupertypeHierarchy(IProgressMonitor monitor) throws RubyModelException { return this.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, monitor); } /** * @see IType#newSupertypeHierarchy(WorkingCopyOwner, IProgressMonitor) */ public ITypeHierarchy newSupertypeHierarchy( WorkingCopyOwner owner, IProgressMonitor monitor) throws RubyModelException { IRubyScript[] workingCopies = RubyModelManager.getRubyModelManager().getWorkingCopies(owner, true/*add primary working copies*/); CreateTypeHierarchyOperation op= new CreateTypeHierarchyOperation(this, workingCopies, SearchEngine.createWorkspaceScope(), false); op.runOperation(monitor); return op.getResult(); } public IMethod[] findMethods(IMethod method) { List<IMethod> filtered = new ArrayList<IMethod>(); try { ArrayList<IRubyElement> list = getChildrenOfType(METHOD); for (IRubyElement element : list) { IMethod other = (IMethod) element; if (!other.getElementName().equals(method.getElementName())) continue; filtered.add(other); } } catch (RubyModelException e) { RubyCore.log(e); } IMethod[] array = new IMethod[filtered.size()]; filtered.toArray(array); return array; } }