|
From: <caw...@us...> - 2007-05-01 14:53:46
|
Revision: 2406
http://svn.sourceforge.net/rubyeclipse/?rev=2406&view=rev
Author: cawilliams
Date: 2007-05-01 07:53:45 -0700 (Tue, 01 May 2007)
Log Message:
-----------
change our existing OpenTypeAction to use the new TypeSelectionDialog2 which is backed by the SearchEngine. Now we can do much quicker search of type names, keep track of thse user picked, do external library types, etc.
Modified Paths:
--------------
trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/RubyModelUtil.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/Strings.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPlugin.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPluginImages.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyUIMessages.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyUIMessages.properties
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/PreferenceConstants.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/RubyElementLabels.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/actions/OpenTypeAction.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.ui/icons/full/dlcl16/view_menu.gif
trunk/org.rubypeople.rdt.ui/icons/full/elcl16/view_menu.gif
trunk/org.rubypeople.rdt.ui/icons/full/obj16/type_separator.gif
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.properties
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/ExternalFileTypeInfo.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/History.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/IFileTypeInfo.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/OpenTypeHistory.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/SearchUtils.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeFilter.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfo.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfoFactory.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfoFilter.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/UnresolvableTypeInfo.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/dialogs/OpenTypeSelectionDialog2.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/dialogs/TextFieldNavigationHandler.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/dialogs/TypeInfoViewer.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/dialogs/TypeSelectionComponent.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/dialogs/TypeSelectionDialog2.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/RubyBreakIterator.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/RubyWordIterator.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/SequenceCharacterIterator.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/util/TypeInfoLabelProvider.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/dialogs/ITypeInfoFilterExtension.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/dialogs/ITypeInfoImageProvider.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/dialogs/ITypeInfoRequestor.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/dialogs/ITypeSelectionComponent.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/dialogs/TypeSelectionExtension.java
Modified: trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF
===================================================================
--- trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF 2007-05-01 14:52:12 UTC (rev 2405)
+++ trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF 2007-05-01 14:53:45 UTC (rev 2406)
@@ -58,6 +58,7 @@
org.eclipse.compare,
org.eclipse.core.filesystem,
org.eclipse.core.expressions,
- org.eclipse.ltk.core.refactoring
+ org.eclipse.ltk.core.refactoring,
+ com.ibm.icu
Eclipse-LazyStart: true
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Added: trunk/org.rubypeople.rdt.ui/icons/full/dlcl16/view_menu.gif
===================================================================
(Binary files differ)
Property changes on: trunk/org.rubypeople.rdt.ui/icons/full/dlcl16/view_menu.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/org.rubypeople.rdt.ui/icons/full/elcl16/view_menu.gif
===================================================================
(Binary files differ)
Property changes on: trunk/org.rubypeople.rdt.ui/icons/full/elcl16/view_menu.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/org.rubypeople.rdt.ui/icons/full/obj16/type_separator.gif
===================================================================
(Binary files differ)
Property changes on: trunk/org.rubypeople.rdt.ui/icons/full/obj16/type_separator.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,16 @@
+package org.rubypeople.rdt.internal.corext.util;
+
+import org.eclipse.osgi.util.NLS;
+
+public class CorextMessages extends NLS {
+
+ private static final String BUNDLE_NAME = CorextMessages.class.getName();
+
+ public static String History_error_serialize;
+ public static String History_error_read;
+ public static String TypeInfoHistory_consistency_check;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, CorextMessages.class);
+ }
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.properties
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.properties (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/CorextMessages.properties 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,3 @@
+History_error_serialize= Problems serializing information to XML ''{0}''
+TypeInfoHistory_consistency_check=Checking consistency of type history...
+History_error_read=Problems reading information from XML ''{0}''
\ No newline at end of file
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/ExternalFileTypeInfo.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/ExternalFileTypeInfo.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/ExternalFileTypeInfo.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.corext.util;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyModel;
+import org.rubypeople.rdt.core.IRubyProject;
+import org.rubypeople.rdt.core.ISourceFolder;
+import org.rubypeople.rdt.core.ISourceFolderRoot;
+import org.rubypeople.rdt.core.RubyCore;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.core.search.IRubySearchScope;
+
+/**
+ * A <tt>ExternalFileTypeInfo</tt> represents a type in a Jar file.
+ */
+public class ExternalFileTypeInfo extends TypeInfo {
+
+ private final String fPath;
+
+ public ExternalFileTypeInfo(String pkg, String name, char[][] enclosingTypes, boolean isModule, String path) {
+ super(pkg, name, enclosingTypes, isModule);
+ fPath = path;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!ExternalFileTypeInfo.class.equals(obj.getClass()))
+ return false;
+ ExternalFileTypeInfo other= (ExternalFileTypeInfo)obj;
+ return doEquals(other) && fPath.equals(other.fPath);
+ }
+
+ public int getElementType() {
+ return TypeInfo.JAR_FILE_ENTRY_TYPE_INFO;
+ }
+
+ protected IRubyElement getContainer(IRubySearchScope scope) throws RubyModelException {
+ IRubyModel jmodel= RubyCore.create(ResourcesPlugin.getWorkspace().getRoot());
+ IPath[] enclosedPaths= scope.enclosingProjectsAndJars();
+
+ // TODO Remove the last segment of the path, it's the filename. Also remove "package names" from path
+ String rootPath = new File(fPath).getParent();
+ for (int i= 0; i < enclosedPaths.length; i++) {
+ IPath curr= enclosedPaths[i];
+ if (curr.segmentCount() == 1) {
+ IRubyProject jproject= jmodel.getRubyProject(curr.segment(0));
+ ISourceFolderRoot root= jproject.getSourceFolderRoot(rootPath);
+ if (root.exists()) {
+ return findElementInRoot(root);
+ }
+ }
+ }
+ List paths= Arrays.asList(enclosedPaths);
+ IRubyProject[] projects= jmodel.getRubyProjects();
+ for (int i= 0; i < projects.length; i++) {
+ IRubyProject jproject= projects[i];
+ if (!paths.contains(jproject.getPath())) {
+ ISourceFolderRoot root= jproject.getSourceFolderRoot(fPath);
+ if (root.exists()) {
+ return findElementInRoot(root);
+ }
+ }
+ }
+ return null;
+ }
+
+ private IRubyElement findElementInRoot(ISourceFolderRoot root) {
+ IRubyElement res;
+ ISourceFolder frag= root.getSourceFolder(getPackageName());
+ String extension= getExtension();
+ String fullName= getFileName() + '.' + extension;
+
+ if (RubyCore.isRubyLikeFileName(fullName)) {
+ res= frag.getRubyScript(fullName);
+ } else {
+ return null;
+ }
+ if (res.exists()) {
+ return res;
+ }
+ return null;
+ }
+
+ private String getFileName() {
+ String name = new File(fPath).getName();
+ return name.substring(0, name.lastIndexOf('.'));
+ }
+
+ private String getExtension() {
+ String name = new File(fPath).getName();
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+
+ public IPath getPackageFragmentRootPath() {
+ return new Path(fPath);
+ }
+
+ public String getPackageFragmentRootName() {
+ // we can't remove the '/' since the jar can be external.
+ return fPath;
+ }
+
+ public String getPath() {
+ StringBuffer result= new StringBuffer(fPath);
+// result.append(IRubySearchScope.JAR_FILE_ENTRY_SEPARATOR);
+ getElementPath(result);
+ return result.toString();
+ }
+
+ public long getContainerTimestamp() {
+ // First try internal Jar
+ IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+ IPath path= new Path(fPath);
+ IResource resource= root.findMember(path);
+ IFileInfo info= null;
+ IRubyElement element= null;
+ if (resource != null && resource.exists()) {
+ URI location= resource.getLocationURI();
+ if (location != null) {
+ try {
+ info= EFS.getStore(location).fetchInfo();
+ if (info.exists()) {
+ element= RubyCore.create(resource);
+ // The exist test for external jars is expensive due to
+ // JDT/Core. So do the test here since we know that the
+ // Ruby element points to an internal Jar.
+ if (element != null && !element.exists())
+ element= null;
+ }
+ } catch (CoreException e) {
+ // Fall through
+ }
+ }
+ } else {
+ info= EFS.getLocalFileSystem().getStore(Path.fromOSString(fPath)).fetchInfo();
+ if (info.exists()) {
+ element= getPackageFragementRootForExternalJar();
+ }
+ }
+ if (info != null && info.exists() && element != null) {
+ return info.getLastModified();
+ }
+ return IResource.NULL_STAMP;
+ }
+
+ public boolean isContainerDirty() {
+ return false;
+ }
+
+ private void getElementPath(StringBuffer result) {
+ String pack= getPackageName();
+ if (pack != null && pack.length() > 0) {
+ result.append(pack.replace(TypeInfo.PACKAGE_PART_SEPARATOR, TypeInfo.SEPARATOR));
+ result.append(TypeInfo.SEPARATOR);
+ }
+ result.append(getFileName());
+ result.append('.');
+ result.append(getExtension());
+ }
+
+ private ISourceFolderRoot getPackageFragementRootForExternalJar() {
+ try {
+ IRubyModel jmodel= RubyCore.create(ResourcesPlugin.getWorkspace().getRoot());
+ IRubyProject[] projects= jmodel.getRubyProjects();
+ for (int i= 0; i < projects.length; i++) {
+ IRubyProject project= projects[i];
+ ISourceFolderRoot root= project.getSourceFolderRoot(fPath);
+ // Cheaper check than calling root.exists().
+ if (project.isOnLoadpath(root))
+ return root;
+ }
+ } catch (RubyModelException e) {
+ // Fall through
+ }
+ return null;
+ }
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/History.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/History.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/History.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.corext.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.RubyUIException;
+import org.rubypeople.rdt.internal.ui.RubyUIStatus;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * History stores a list of key, object pairs. The list is bounded at size
+ * MAX_HISTORY_SIZE. If the list exceeds this size the eldest element is removed
+ * from the list. An element can be added/renewed with a call to <code>accessed(Object)</code>.
+ *
+ * The history can be stored to/loaded from an xml file.
+ */
+public abstract class History {
+
+ private static final String DEFAULT_ROOT_NODE_NAME= "histroyRootNode"; //$NON-NLS-1$
+ private static final String DEFAULT_INFO_NODE_NAME= "infoNode"; //$NON-NLS-1$
+ private static final int MAX_HISTORY_SIZE= 60;
+
+ private static RubyUIException createException(Throwable t, String message) {
+ return new RubyUIException(RubyUIStatus.createError(IStatus.ERROR, message, t));
+ }
+
+ private final Map fHistory;
+ private final Hashtable fPositions;
+ private final String fFileName;
+ private final String fRootNodeName;
+ private final String fInfoNodeName;
+
+ public History(String fileName, String rootNodeName, String infoNodeName) {
+ fHistory= new LinkedHashMap(80, 0.75f, true) {
+ private static final long serialVersionUID= 1L;
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > MAX_HISTORY_SIZE;
+ }
+ };
+ fFileName= fileName;
+ fRootNodeName= rootNodeName;
+ fInfoNodeName= infoNodeName;
+ fPositions= new Hashtable(MAX_HISTORY_SIZE);
+ }
+
+ public History(String fileName) {
+ this(fileName, DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
+ }
+
+ public synchronized void accessed(Object object) {
+ fHistory.put(getKey(object), object);
+ rebuildPositions();
+ }
+
+ public synchronized boolean contains(Object object) {
+ return fHistory.containsKey(getKey(object));
+ }
+
+ public synchronized boolean containsKey(Object key) {
+ return fHistory.containsKey(key);
+ }
+
+ public synchronized boolean isEmpty() {
+ return fHistory.isEmpty();
+ }
+
+ public synchronized Object remove(Object object) {
+ Object removed= fHistory.remove(getKey(object));
+ rebuildPositions();
+ return removed;
+ }
+
+ public synchronized Object removeKey(Object key) {
+ Object removed= fHistory.remove(key);
+ rebuildPositions();
+ return removed;
+ }
+
+ /**
+ * Normalized position in history of object denoted by key.
+ * The position is a value between zero and one where zero
+ * means not contained in history and one means newest element
+ * in history. The lower the value the older the element.
+ *
+ * @param key The key of the object to inspect
+ * @return value in [0.0, 1.0] the lower the older the element
+ */
+ public synchronized float getNormalizedPosition(Object key) {
+ if (!containsKey(key))
+ return 0.0f;
+
+ int pos= ((Integer)fPositions.get(key)).intValue() + 1;
+
+ //containsKey(key) implies fHistory.size()>0
+ return (float)pos / (float)fHistory.size();
+ }
+
+ /**
+ * Absolute position of object denoted by key in the
+ * history or -1 if !containsKey(key). The higher the
+ * newer.
+ *
+ * @param key The key of the object to inspect
+ * @return value between 0 and MAX_HISTORY_SIZE - 1, or -1
+ */
+ public synchronized int getPosition(Object key) {
+ if (!containsKey(key))
+ return -1;
+
+ return ((Integer)fPositions.get(key)).intValue();
+ }
+
+ public synchronized void load() {
+ IPath stateLocation= RubyPlugin.getDefault().getStateLocation().append(fFileName);
+ File file= new File(stateLocation.toOSString());
+ if (file.exists()) {
+ InputStreamReader reader= null;
+ try {
+ reader = new InputStreamReader(new FileInputStream(file), "utf-8");//$NON-NLS-1$
+ load(new InputSource(reader));
+ } catch (IOException e) {
+ RubyPlugin.log(e);
+ } catch (CoreException e) {
+ RubyPlugin.log(e);
+ } finally {
+ try {
+ if (reader != null)
+ reader.close();
+ } catch (IOException e) {
+ RubyPlugin.log(e);
+ }
+ }
+ }
+ }
+
+ public synchronized void save() {
+ IPath stateLocation= RubyPlugin.getDefault().getStateLocation().append(fFileName);
+ File file= new File(stateLocation.toOSString());
+ OutputStream out= null;
+ try {
+ out= new FileOutputStream(file);
+ save(out);
+ } catch (IOException e) {
+ RubyPlugin.log(e);
+ } catch (CoreException e) {
+ RubyPlugin.log(e);
+ } catch (TransformerFactoryConfigurationError e) {
+ // The XML library can be misconficgured (e.g. via
+ // -Djava.endorsed.dirs=C:\notExisting\xerces-2_7_1)
+ RubyPlugin.log(e);
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ RubyPlugin.log(e);
+ }
+ }
+ }
+
+ protected Set getKeys() {
+ return fHistory.keySet();
+ }
+
+ protected Collection getValues() {
+ return fHistory.values();
+ }
+
+ /**
+ * Store <code>Object</code> in <code>Element</code>
+ *
+ * @param object The object to store
+ * @param element The Element to store to
+ */
+ protected abstract void setAttributes(Object object, Element element);
+
+ /**
+ * Return a new instance of an Object given <code>element</code>
+ *
+ * @param element The element containing required information to create the Object
+ */
+ protected abstract Object createFromElement(Element element);
+
+ /**
+ * Get key for object
+ *
+ * @param object The object to calculate a key for, not null
+ * @return The key for object, not null
+ */
+ protected abstract Object getKey(Object object);
+
+ private void rebuildPositions() {
+ fPositions.clear();
+ Collection values= fHistory.values();
+ int pos=0;
+ for (Iterator iter= values.iterator(); iter.hasNext();) {
+ Object element= iter.next();
+ fPositions.put(getKey(element), new Integer(pos));
+ pos++;
+ }
+ }
+
+ private void load(InputSource inputSource) throws CoreException {
+ Element root;
+ try {
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ root = parser.parse(inputSource).getDocumentElement();
+ } catch (SAXException e) {
+ throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName));
+ } catch (ParserConfigurationException e) {
+ throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName));
+ } catch (IOException e) {
+ throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName));
+ }
+
+ if (root == null) return;
+ if (!root.getNodeName().equalsIgnoreCase(fRootNodeName)) {
+ return;
+ }
+ NodeList list= root.getChildNodes();
+ int length= list.getLength();
+ for (int i= 0; i < length; ++i) {
+ Node node= list.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element type= (Element) node;
+ if (type.getNodeName().equalsIgnoreCase(fInfoNodeName)) {
+ Object object= createFromElement(type);
+ fHistory.put(getKey(object), object);
+ }
+ }
+ }
+ rebuildPositions();
+ }
+
+ private void save(OutputStream stream) throws CoreException {
+ try {
+ DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder= factory.newDocumentBuilder();
+ Document document= builder.newDocument();
+
+ Element rootElement = document.createElement(fRootNodeName);
+ document.appendChild(rootElement);
+
+ Iterator values= getValues().iterator();
+ while (values.hasNext()) {
+ Object object= values.next();
+ Element element= document.createElement(fInfoNodeName);
+ setAttributes(object, element);
+ rootElement.appendChild(element);
+ }
+
+ Transformer transformer=TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(stream);
+
+ transformer.transform(source, result);
+ } catch (TransformerException e) {
+ throw createException(e, Messages.format(CorextMessages.History_error_serialize, fFileName));
+ } catch (ParserConfigurationException e) {
+ throw createException(e, Messages.format(CorextMessages.History_error_serialize, fFileName));
+ }
+ }
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/IFileTypeInfo.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/IFileTypeInfo.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/IFileTypeInfo.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.corext.util;
+
+import java.net.URI;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.RubyCore;
+import org.rubypeople.rdt.core.search.IRubySearchScope;
+
+/**
+ * A <tt>IFileTypeInfo</tt> represents a type in a class or java file.
+ */
+public class IFileTypeInfo extends TypeInfo {
+
+ private final String fProject;
+ private final String fFolder;
+ private final String fFile;
+ private final String fExtension;
+
+ public IFileTypeInfo(String pkg, String name, char[][] enclosingTypes, boolean isModule, String project, String sourceFolder, String file, String extension) {
+ super(pkg, name, enclosingTypes, isModule);
+ fProject= project;
+ fFolder= sourceFolder;
+ fFile= file;
+ fExtension= extension;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!IFileTypeInfo.class.equals(obj.getClass()))
+ return false;
+ IFileTypeInfo other= (IFileTypeInfo)obj;
+ return doEquals(other) && fProject.equals(other.fProject) && equals(fFolder, other.fFolder) &&
+ fFile.equals(other.fFile) && fExtension.equals(other.fExtension);
+ }
+
+ public int getElementType() {
+ return TypeInfo.IFILE_TYPE_INFO;
+ }
+
+ protected IRubyElement getContainer(IRubySearchScope scope) {
+ IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+ IPath path= new Path(getPath());
+ IResource resource= root.findMember(path);
+ if (resource != null) {
+ IRubyElement elem= RubyCore.create(resource);
+ if (elem != null && elem.exists()) {
+ return elem;
+ }
+ }
+ return null;
+ }
+
+ public IPath getPackageFragmentRootPath() {
+ StringBuffer buffer= new StringBuffer();
+ buffer.append(TypeInfo.SEPARATOR);
+ buffer.append(fProject);
+ if (fFolder != null && fFolder.length() > 0) {
+ buffer.append(TypeInfo.SEPARATOR);
+ buffer.append(fFolder);
+ }
+ return new Path(buffer.toString());
+ }
+
+ public String getPackageFragmentRootName() {
+ StringBuffer buffer= new StringBuffer();
+ buffer.append(fProject);
+ if (fFolder != null && fFolder.length() > 0) {
+ buffer.append(TypeInfo.SEPARATOR);
+ buffer.append(fFolder);
+ }
+ return buffer.toString();
+ }
+
+ public String getPath() {
+ StringBuffer result= new StringBuffer();
+ result.append(TypeInfo.SEPARATOR);
+ result.append(fProject);
+ result.append(TypeInfo.SEPARATOR);
+ if (fFolder != null && fFolder.length() > 0) {
+ result.append(fFolder);
+ result.append(TypeInfo.SEPARATOR);
+ }
+ if (fPackage != null && fPackage.length() > 0) {
+ result.append(fPackage.replace(TypeInfo.PACKAGE_PART_SEPARATOR, TypeInfo.SEPARATOR));
+ result.append(TypeInfo.SEPARATOR);
+ }
+ result.append(fFile);
+ result.append('.');
+ result.append(fExtension);
+ return result.toString();
+ }
+
+ public String getProject() {
+ return fProject;
+ }
+
+ public String getFolder() {
+ return fFolder;
+ }
+
+ public String getFileName() {
+ return fFile;
+ }
+
+ public String getExtension() {
+ return fExtension;
+ }
+
+ public long getContainerTimestamp() {
+ IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+ IPath path= new Path(getPath());
+ IResource resource= root.findMember(path);
+ if (resource != null) {
+ URI location= resource.getLocationURI();
+ if (location != null) {
+ try {
+ IFileInfo info= EFS.getStore(location).fetchInfo();
+ if (info.exists()) {
+ // The element could be removed from the build path. So check
+ // if the Ruby element still exists.
+ IRubyElement element= RubyCore.create(resource);
+ if (element != null && element.exists())
+ return info.getLastModified();
+ }
+ } catch (CoreException e) {
+ // Fall through
+ }
+ }
+ }
+ return IResource.NULL_STAMP;
+ }
+
+ public boolean isContainerDirty() {
+ IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+ IPath path= new Path(getPath());
+ IResource resource= root.findMember(path);
+ ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
+ ITextFileBuffer textFileBuffer= manager.getTextFileBuffer(resource.getFullPath());
+ if (textFileBuffer != null) {
+ return textFileBuffer.isDirty();
+ }
+ return false;
+ }
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/OpenTypeHistory.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/OpenTypeHistory.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/OpenTypeHistory.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,369 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+ package org.rubypeople.rdt.internal.corext.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.rubypeople.rdt.core.ElementChangedEvent;
+import org.rubypeople.rdt.core.IElementChangedListener;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyElementDelta;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.IType;
+import org.rubypeople.rdt.core.RubyCore;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.core.search.IRubySearchScope;
+import org.rubypeople.rdt.core.search.SearchEngine;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.w3c.dom.Element;
+
+/**
+ * History for the open type dialog. Object and keys are both {@link TypeInfo}s.
+ */
+public class OpenTypeHistory extends History {
+
+ private static class TypeHistoryDeltaListener implements IElementChangedListener {
+ public void elementChanged(ElementChangedEvent event) {
+ if (processDelta(event.getDelta())) {
+ OpenTypeHistory.getInstance().markAsInconsistent();
+ }
+ }
+
+ /**
+ * Computes whether the history needs a consistency check or not.
+ *
+ * @param delta the Ruby element delta
+ *
+ * @return <code>true</code> if consistency must be checked
+ * <code>false</code> otherwise.
+ */
+ private boolean processDelta(IRubyElementDelta delta) {
+ IRubyElement elem= delta.getElement();
+
+ boolean isChanged= delta.getKind() == IRubyElementDelta.CHANGED;
+ boolean isRemoved= delta.getKind() == IRubyElementDelta.REMOVED;
+
+ switch (elem.getElementType()) {
+ case IRubyElement.RUBY_PROJECT:
+ if (isRemoved || (isChanged &&
+ (delta.getFlags() & IRubyElementDelta.F_CLOSED) != 0)) {
+ return true;
+ }
+ return processChildrenDelta(delta);
+ case IRubyElement.SOURCE_FOLDER_ROOT:
+ if (isRemoved || (isChanged && (
+ (delta.getFlags() & IRubyElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0 ||
+ (delta.getFlags() & IRubyElementDelta.F_REMOVED_FROM_CLASSPATH) != 0))) {
+ return true;
+ }
+ return processChildrenDelta(delta);
+ case IRubyElement.TYPE:
+ if (isChanged && (delta.getFlags() & IRubyElementDelta.F_MODIFIERS) != 0) {
+ return true;
+ }
+ // type children can be inner classes: fall through
+ case IRubyElement.RUBY_MODEL:
+ case IRubyElement.SOURCE_FOLDER:
+ if (isRemoved) {
+ return true;
+ }
+ return processChildrenDelta(delta);
+ case IRubyElement.SCRIPT:
+ // Not the primary compilation unit. Ignore it
+ if (!RubyModelUtil.isPrimary((IRubyScript) elem)) {
+ return false;
+ }
+
+ if (isRemoved || (isChanged && isUnknownStructuralChange(delta.getFlags()))) {
+ return true;
+ }
+ return processChildrenDelta(delta);
+ default:
+ // fields, methods, imports ect
+ return false;
+ }
+ }
+
+ private boolean isUnknownStructuralChange(int flags) {
+ if ((flags & IRubyElementDelta.F_CONTENT) == 0)
+ return false;
+ return (flags & IRubyElementDelta.F_FINE_GRAINED) == 0;
+ }
+
+ /*
+ private boolean isPossibleStructuralChange(int flags) {
+ return (flags & (IRubyElementDelta.F_CONTENT | IRubyElementDelta.F_FINE_GRAINED)) == IRubyElementDelta.F_CONTENT;
+ }
+ */
+
+ private boolean processChildrenDelta(IRubyElementDelta delta) {
+ IRubyElementDelta[] children= delta.getAffectedChildren();
+ for (int i= 0; i < children.length; i++) {
+ if (processDelta(children[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class UpdateJob extends Job {
+ public static final String FAMILY= UpdateJob.class.getName();
+ public UpdateJob() {
+ super(CorextMessages.TypeInfoHistory_consistency_check);
+ }
+ protected IStatus run(IProgressMonitor monitor) {
+ OpenTypeHistory history= OpenTypeHistory.getInstance();
+ history.internalCheckConsistency(monitor);
+ return new Status(IStatus.OK, RubyPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$
+ }
+ public boolean belongsTo(Object family) {
+ return FAMILY.equals(family);
+ }
+ }
+
+ // Needs to be volatile since accesses aren't synchronized.
+ private volatile boolean fNeedsConsistencyCheck;
+ // Map of cached time stamps
+ private Map fTimestampMapping;
+
+ private final IElementChangedListener fDeltaListener;
+ private final UpdateJob fUpdateJob;
+ private final TypeInfoFactory fTypeInfoFactory;
+
+ private static final String FILENAME= "OpenTypeHistory.xml"; //$NON-NLS-1$
+ private static final String NODE_ROOT= "typeInfoHistroy"; //$NON-NLS-1$
+ private static final String NODE_TYPE_INFO= "typeInfo"; //$NON-NLS-1$
+ private static final String NODE_NAME= "name"; //$NON-NLS-1$
+ private static final String NODE_PACKAGE= "package"; //$NON-NLS-1$
+ private static final String NODE_ENCLOSING_NAMES= "enclosingTypes"; //$NON-NLS-1$
+ private static final String NODE_PATH= "path"; //$NON-NLS-1$
+ private static final String NODE_MODIFIERS= "modifiers"; //$NON-NLS-1$
+ private static final String NODE_TIMESTAMP= "timestamp"; //$NON-NLS-1$
+ private static final char[][] EMPTY_ENCLOSING_NAMES= new char[0][0];
+
+ private static OpenTypeHistory fgInstance;
+
+ public static synchronized OpenTypeHistory getInstance() {
+ if (fgInstance == null)
+ fgInstance= new OpenTypeHistory();
+ return fgInstance;
+ }
+
+ public static synchronized void shutdown() {
+ if (fgInstance == null)
+ return;
+ fgInstance.doShutdown();
+ }
+
+ private OpenTypeHistory() {
+ super(FILENAME, NODE_ROOT, NODE_TYPE_INFO);
+ fTypeInfoFactory= new TypeInfoFactory();
+ fTimestampMapping= new HashMap();
+ fNeedsConsistencyCheck= true;
+ load();
+ fDeltaListener= new TypeHistoryDeltaListener();
+ RubyCore.addElementChangedListener(fDeltaListener);
+ fUpdateJob= new UpdateJob();
+ // It is not necessary anymore that the update job has a rule since
+ // markAsInconsistent isn't synchronized anymore. See bugs
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=128399 and
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=135278
+ // for details.
+ fUpdateJob.setPriority(Job.SHORT);
+ }
+
+ public void markAsInconsistent() {
+ fNeedsConsistencyCheck= true;
+ // cancel the old job. If no job is running this is a NOOP.
+ fUpdateJob.cancel();
+ fUpdateJob.schedule();
+ }
+
+ public boolean needConsistencyCheck() {
+ return fNeedsConsistencyCheck;
+ }
+
+ public void checkConsistency(IProgressMonitor monitor) throws OperationCanceledException {
+ if (!fNeedsConsistencyCheck)
+ return;
+ if (fUpdateJob.getState() == Job.RUNNING) {
+ try {
+ Platform.getJobManager().join(UpdateJob.FAMILY, monitor);
+ } catch (OperationCanceledException e) {
+ // Ignore and do the consistency check without
+ // waiting for the update job.
+ } catch (InterruptedException e) {
+ // Ignore and do the consistency check without
+ // waiting for the update job.
+ }
+ }
+ if (!fNeedsConsistencyCheck)
+ return;
+ internalCheckConsistency(monitor);
+ }
+
+ public synchronized boolean contains(TypeInfo type) {
+ return super.contains(type);
+ }
+
+ public synchronized void accessed(TypeInfo info) {
+ // Fetching the timestamp might not be cheap (remote file system
+ // external Jars. So check if we alreay have one.
+ if (!fTimestampMapping.containsKey(info)) {
+ fTimestampMapping.put(info, new Long(info.getContainerTimestamp()));
+ }
+ super.accessed(info);
+ }
+
+ public synchronized TypeInfo remove(TypeInfo info) {
+ fTimestampMapping.remove(info);
+ return (TypeInfo)super.remove(info);
+ }
+
+ public synchronized TypeInfo[] getTypeInfos() {
+ Collection values= getValues();
+ int size= values.size();
+ TypeInfo[] result= new TypeInfo[size];
+ int i= size - 1;
+ for (Iterator iter= values.iterator(); iter.hasNext();) {
+ result[i]= (TypeInfo)iter.next();
+ i--;
+ }
+ return result;
+ }
+
+ public synchronized TypeInfo[] getFilteredTypeInfos(TypeInfoFilter filter) {
+ Collection values= getValues();
+ List result= new ArrayList();
+ for (Iterator iter= values.iterator(); iter.hasNext();) {
+ TypeInfo type= (TypeInfo)iter.next();
+ if ((filter == null || filter.matchesHistoryElement(type)) && !TypeFilter.isFiltered(type.getFullyQualifiedName()))
+ result.add(type);
+ }
+ Collections.reverse(result);
+ return (TypeInfo[])result.toArray(new TypeInfo[result.size()]);
+
+ }
+
+ protected Object getKey(Object object) {
+ return object;
+ }
+
+ private synchronized void internalCheckConsistency(IProgressMonitor monitor) throws OperationCanceledException {
+ // Setting fNeedsConsistencyCheck is necessary here since
+ // markAsInconsistent isn't synchronized.
+ fNeedsConsistencyCheck= true;
+ IRubySearchScope scope= SearchEngine.createWorkspaceScope();
+ List typesToCheck= new ArrayList(getKeys());
+ monitor.beginTask(CorextMessages.TypeInfoHistory_consistency_check, typesToCheck.size());
+ monitor.setTaskName(CorextMessages.TypeInfoHistory_consistency_check);
+ for (Iterator iter= typesToCheck.iterator(); iter.hasNext();) {
+ TypeInfo type= (TypeInfo)iter.next();
+ long currentTimestamp= type.getContainerTimestamp();
+ Long lastTested= (Long)fTimestampMapping.get(type);
+ if (lastTested != null && currentTimestamp != IResource.NULL_STAMP && currentTimestamp == lastTested.longValue() && !type.isContainerDirty())
+ continue;
+ try {
+ IType jType= type.resolveType(scope);
+ if (jType == null || !jType.exists()) {
+ remove(type);
+ } else {
+ // copy over the modifiers since they may have changed
+ type.setIsModule(jType.isModule());
+ fTimestampMapping.put(type, new Long(currentTimestamp));
+ }
+ } catch (RubyModelException e) {
+ remove(type);
+ }
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ monitor.worked(1);
+ }
+ monitor.done();
+ fNeedsConsistencyCheck= false;
+ }
+
+ private void doShutdown() {
+ RubyCore.removeElementChangedListener(fDeltaListener);
+ save();
+ }
+
+ protected Object createFromElement(Element type) {
+ String name= type.getAttribute(NODE_NAME);
+ String pack= type.getAttribute(NODE_PACKAGE);
+ char[][] enclosingNames= getEnclosingNames(type);
+ String path= type.getAttribute(NODE_PATH);
+ boolean isModule = false;
+ try {
+ isModule= Boolean.parseBoolean(type.getAttribute(NODE_MODIFIERS));
+ } catch (NumberFormatException e) {
+ // take zero
+ }
+ TypeInfo info= fTypeInfoFactory.create(
+ pack.toCharArray(), name.toCharArray(), enclosingNames, isModule, path);
+ long timestamp= IResource.NULL_STAMP;
+ String timestampValue= type.getAttribute(NODE_TIMESTAMP);
+ if (timestampValue != null && timestampValue.length() > 0) {
+ try {
+ timestamp= Long.parseLong(timestampValue);
+ } catch (NumberFormatException e) {
+ // take null stamp
+ }
+ }
+ if (timestamp != IResource.NULL_STAMP) {
+ fTimestampMapping.put(info, new Long(timestamp));
+ }
+ return info;
+ }
+
+ protected void setAttributes(Object object, Element typeElement) {
+ TypeInfo type= (TypeInfo)object;
+ typeElement.setAttribute(NODE_NAME, type.getTypeName());
+ typeElement.setAttribute(NODE_PACKAGE, type.getPackageName());
+ typeElement.setAttribute(NODE_ENCLOSING_NAMES, type.getEnclosingName());
+ typeElement.setAttribute(NODE_PATH, type.getPath());
+ typeElement.setAttribute(NODE_MODIFIERS, Boolean.toString(type.isModule()));
+ Long timestamp= (Long) fTimestampMapping.get(type);
+ if (timestamp == null) {
+ typeElement.setAttribute(NODE_TIMESTAMP, Long.toString(IResource.NULL_STAMP));
+ } else {
+ typeElement.setAttribute(NODE_TIMESTAMP, timestamp.toString());
+ }
+ }
+
+ private char[][] getEnclosingNames(Element type) {
+ String enclosingNames= type.getAttribute(NODE_ENCLOSING_NAMES);
+ if (enclosingNames.length() == 0)
+ return EMPTY_ENCLOSING_NAMES;
+ StringTokenizer tokenizer= new StringTokenizer(enclosingNames, "."); //$NON-NLS-1$
+ List names= new ArrayList();
+ while(tokenizer.hasMoreTokens()) {
+ String name= tokenizer.nextToken();
+ names.add(name.toCharArray());
+ }
+ return (char[][])names.toArray(new char[names.size()][]);
+ }
+}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/RubyModelUtil.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/RubyModelUtil.java 2007-05-01 14:52:12 UTC (rev 2405)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/RubyModelUtil.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -7,6 +7,7 @@
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.ISourceFolderRoot;
+import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.core.util.CharOperation;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
@@ -82,4 +83,60 @@
}
return false;
}
+
+ /**
+ * Concatenates two names. Uses a '/' for separation.
+ * Both strings can be empty or <code>null</code>.
+ */
+ public static String concatenateName(char[] name1, char[] name2) {
+ StringBuffer buf= new StringBuffer();
+ if (name1 != null && name1.length > 0) {
+ buf.append(name1);
+ }
+ if (name2 != null && name2.length > 0) {
+ if (buf.length() > 0) {
+ buf.append("/");
+ }
+ buf.append(name2);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns the fully qualified name of the given type using '::' as separators.
+ * This is a replace for IType.getFullyQualifiedTypeName
+ * which uses '$' as separators. As '$' is also a valid character in an id
+ * this is ambiguous.
+ */
+ public static String getFullyQualifiedName(IType type) {
+ return type.getFullyQualifiedName();
+ }
+
+ /**
+ * Finds a type in a ruby script. Typical usage is to find the corresponding
+ * type in a working copy.
+ * @param script the compilation unit to search in
+ * @param typeQualifiedName the type qualified name (type name with enclosing type names (separated by dots))
+ * @return the type found, or null if not existing
+ */
+ public static IType findTypeInRubyScript(IRubyScript script, String typeQualifiedName) throws RubyModelException {
+ IType[] types= script.getAllTypes();
+ for (int i= 0; i < types.length; i++) {
+ String currName= getTypeQualifiedName(types[i]);
+ if (typeQualifiedName.equals(currName)) {
+ return types[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the qualified type name of the given type using '.' as separators.
+ * This is a replace for IType.getTypeQualifiedName()
+ * which uses '$' as separators. As '$' is also a valid character in an id
+ * this is ambiguous. JavaCore PR: 1GCFUNT
+ */
+ public static String getTypeQualifiedName(IType type) {
+ return type.getTypeQualifiedName("::");
+ }
}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/SearchUtils.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/SearchUtils.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/SearchUtils.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,19 @@
+package org.rubypeople.rdt.internal.corext.util;
+
+import org.rubypeople.rdt.core.search.SearchPattern;
+
+public class SearchUtils {
+
+ /**
+ * Returns whether the given pattern is a camel case pattern or not.
+ *
+ * @param pattern the pattern to inspect
+ * @return whether it is a camel case pattern or not
+ */
+ public static boolean isCamelCasePattern(String pattern) {
+ return SearchPattern.validateMatchRule(
+ pattern,
+ SearchPattern.R_CAMELCASE_MATCH) == SearchPattern.R_CAMELCASE_MATCH;
+ }
+
+}
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/Strings.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/Strings.java 2007-05-01 14:52:12 UTC (rev 2405)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/Strings.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -101,4 +101,47 @@
return true;
}
+ public static boolean equals(String s, char[] c) {
+ if (s.length() != c.length)
+ return false;
+
+ for (int i = c.length; --i >= 0;)
+ if (s.charAt(i) != c[i])
+ return false;
+ return true;
+ }
+
+ public static boolean startsWithIgnoreCase(String text, String prefix) {
+ int textLength= text.length();
+ int prefixLength= prefix.length();
+ if (textLength < prefixLength)
+ return false;
+ for (int i= prefixLength - 1; i >= 0; i--) {
+ if (Character.toLowerCase(prefix.charAt(i)) != Character.toLowerCase(text.charAt(i)))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * tests if a char is lower case. Fix for 26529
+ */
+ public static boolean isLowerCase(char ch) {
+ return Character.toLowerCase(ch) == ch;
+ }
+
+ public static String removeMnemonicIndicator(String string) {
+ int length= string.length();
+ StringBuffer result= new StringBuffer(length);
+ char lastChar= ' '; // everything except & is OK as an initializer
+ for(int i= 0; i < length; i++) {
+ char ch= string.charAt(i);
+ if (ch != '&' || lastChar == '&') {
+ result.append(ch);
+ }
+ lastChar= ch;
+ }
+ return result.toString();
+ }
+
}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeFilter.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeFilter.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeFilter.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.corext.util;
+
+import java.util.StringTokenizer;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.rubypeople.rdt.core.IType;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.util.StringMatcher;
+import org.rubypeople.rdt.ui.PreferenceConstants;
+
+/**
+ *
+ */
+public class TypeFilter implements IPropertyChangeListener {
+
+ public static TypeFilter getDefault() {
+ return RubyPlugin.getDefault().getTypeFilter();
+ }
+
+ public static boolean isFiltered(String fullTypeName) {
+ return getDefault().filter(fullTypeName);
+ }
+
+ public static boolean isFiltered(char[] fullTypeName) {
+ return getDefault().filter(new String(fullTypeName));
+ }
+
+ public static boolean isFiltered(char[] packageName, char[] typeName) {
+ return getDefault().filter(RubyModelUtil.concatenateName(packageName, typeName));
+ }
+
+ public static boolean isFiltered(IType type) {
+ TypeFilter typeFilter = getDefault();
+ if (typeFilter.hasFilters()) {
+ return typeFilter.filter(RubyModelUtil.getFullyQualifiedName(type));
+ }
+ return false;
+ }
+
+
+ private StringMatcher[] fStringMatchers;
+
+ /**
+ *
+ */
+ public TypeFilter() {
+ fStringMatchers= null;
+ PreferenceConstants.getPreferenceStore().addPropertyChangeListener(this);
+ }
+
+ private synchronized StringMatcher[] getStringMatchers() {
+ if (fStringMatchers == null) {
+ String str= PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.TYPEFILTER_ENABLED);
+ StringTokenizer tok= new StringTokenizer(str, ";"); //$NON-NLS-1$
+ int nTokens= tok.countTokens();
+
+ fStringMatchers= new StringMatcher[nTokens];
+ for (int i= 0; i < nTokens; i++) {
+ String curr= tok.nextToken();
+ if (curr.length() > 0) {
+ fStringMatchers[i]= new StringMatcher(curr, false, false);
+ }
+ }
+ }
+ return fStringMatchers;
+ }
+
+ public void dispose() {
+ PreferenceConstants.getPreferenceStore().removePropertyChangeListener(this);
+ fStringMatchers= null;
+ }
+
+
+ public boolean hasFilters() {
+ return getStringMatchers().length > 0;
+ }
+
+ public boolean filter(String fullTypeName) {
+ StringMatcher[] matchers= getStringMatchers();
+ for (int i= 0; i < matchers.length; i++) {
+ StringMatcher curr= matchers[i];
+ if (curr.match(fullTypeName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public synchronized void propertyChange(PropertyChangeEvent event) {
+ if (PreferenceConstants.TYPEFILTER_ENABLED.equals(event.getProperty())) {
+ fStringMatchers= null;
+ }
+ }
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfo.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfo.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/corext/util/TypeInfo.java 2007-05-01 14:53:45 UTC (rev 2406)
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.corext.util;
+
+import org.eclipse.core.runtime.IPath;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.IType;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.core.search.IRubySearchScope;
+import org.rubypeople.rdt.internal.core.util.CharOperation;
+import org.rubypeople.rdt.ui.dialogs.ITypeInfoRequestor;
+
+public abstract class TypeInfo {
+
+ public static class TypeInfoAdapter implements ITypeInfoRequestor {
+ private TypeInfo fInfo;
+ public void setInfo(TypeInfo info) {
+ fInfo= info;
+ }
+ public boolean isModule() {
+ return fInfo.isModule();
+ }
+ public String getTypeName() {
+ return fInfo.getTypeName();
+ }
+ public String getPackageName() {
+ return fInfo.getPackageName();
+ }
+ public String getEnclosingName() {
+ return fInfo.getEnclosingName();
+ }
+ }
+
+ final String fName;
+ final String fPackage;
+ final char[][] fEnclosingNames;
+
+ private boolean fIsModule;
+
+ public static final int UNRESOLVABLE_TYPE_INFO= 1;
+ public static final int JAR_FILE_ENTRY_TYPE_INFO= 2;
+ public static final int IFILE_TYPE_INFO= 3;
+
+ static final char SEPARATOR= '/';
+ static final char EXTENSION_SEPARATOR= '.';
+ static final char PACKAGE_PART_SEPARATOR='.';
+
+ static final String EMPTY_STRING= ""; //$NON-NLS-1$
+
+ protected TypeInfo(String pkg, String name, char[][] enclosingTypes, boolean isModule) {
+ fPackage= pkg;
+ fName= name;
+ fIsModule= isModule;
+ fEnclosingNames= enclosingTypes;
+ }
+
+ public int hashCode() {
+ return (fPackage.hashCode() << 16) + fName.hashCode();
+ }
+
+ /**
+ * Returns this type info's kind encoded as an integer.
+ *
+ * @return the type info's kind
+ */
+ public abstract int getElementType();
+
+ /**
+ * Returns the path reported by the <tt>ITypeNameRequestor</tt>.
+ *
+ * @return the path of the type info
+ */
+ public abstract String getPath();
+
+ /**
+ * Returns the container (class file or CU) this type info is contained
+ * in.
+ *
+ * @param scope the scope used to resolve the <tt>IRubyElement</tt>.
+ * @return the container this type info is contained in.
+ * @throws RubyModelException if an error occurs while access the Ruby
+ * model.
+ */
+ protected abstract IRubyElement getContainer(IRubySearchScope scope) throws RubyModelException;
+
+ /**
+ * Returns the package fragment root path of this type info.
+ *
+ * @return the package fragment root as an <tt>IPath</tt>.
+ */
+ public abstract IPath getPackageFragmentRootPath();
+
+ /**
+ * Returns the package fragment root name of this type info
+ */
+ public abstract String getPackageFragmentRootName();
+
+ /**
+ * Returns the type name.
+ *
+ * @return the info's type name.
+ */
+ public String getTypeName() {
+ return fName;
+ }
+
+ /**
+ * Returns the package name.
+ *
+ * @return the info's package name.
+ */
+ public String getPackageName() {
+ return fPackage;
+ }
+
+ /**
+ * Returns true iff the type info describes an interface.
+ */
+ public boolean isModule() {
+ return fIsModule;
+ }
+
+ /**
+ * Returns true if the info is enclosed in the given scope
+ */
+ public boolean isEnclosed(IRubySearchScope scope) {
+ return scope.encloses(getPath());
+ }
+
+ /**
+ * Gets the enclosing name (dot separated).
+ */
+ public String getEnclosingName() {
+ if (fEnclosingNames == null || fEnclosingNames.length == 0)
+ return EMPTY_STRING;
+ StringBuffer buf= new StringBuffer();
+ for (int i= 0; i < fEnclosingNames.length; i++) {
+ if (i != 0) {
+ buf.append('.');
+ }
+ buf.append(fEnclosingNames[i]);
+ }
+ return buf.toString();
+ }
+
+ public boolean isInnerType() {
+ return fEnclosingNames != null && fEnclosingNames.length > 0;
+ }
+
+ /**
+ * Gets the type qualified name: Includes enclosing type names, but
+ * not package name. Identifiers are separated by dots.
+ */
+ public String getTypeQualifiedName() {
+ if (fEnclosingNames != null && fEnclosingNames.length > 0) {
+ StringBuffer buf= new StringBuffer();
+ for (int i= 0; i < fEnclosingNames.length; i++) {
+ buf.append(fEnclosingNames[i]);
+ buf.append('.');
+ }
+ buf.append(fName);
+ return buf.toString();
+ }
+ return fName;
+ }
+
+ /**
+ * Gets the fully qualified type name: Includes enclosing type names and
+ * package. All identifiers are separated by dots.
+ */
+ public String getFullyQualifiedName() {
+ StringBuffer buf= new StringBuffer();
+ if (fPackage.length() > 0) {
+ buf.append(fPackage);
+ buf.append('.');
+ }
+ if (fEnclosingNames != null) {
+ for (int i= 0; i < fEnclosingNames.length; i++) {
+ buf.append(fEnclosingNames[i]);
+ buf.append('.');
+ }
+ }
+ buf.append(fName);
+ return buf.toString();
+ }
+
+ /**
+ * Gets the fully qualified type container name: Package name or
+ * enclosing type name with package name.
+ * All identifiers are separated by dots.
+ */
+ public String getTypeContainerName() {
+ if (fEnclosingNames != null && fEnclosingNames.length > 0) {
+ StringBuffer buf= new StringBuffer();
+ if (fPackage.length() > 0) {
+ buf.append(fPackage);
+ }
+ for (int i= 0; i < fEnclosingNames.length; i++) {
+ if (buf.length() > 0) {
+ buf.append('.');
+ }
+ buf.append(fEnclosingNames[i]);
+ }
+ return buf.toString();
+ }
+ return fPackage;
+ }
+
+ /**
+ * Resolves the type in a scope if was searched for.
+ * The parent project of JAR files is the first project found in scope.
+ * Returns null if the type could not be resolved
+ */
+ public IType resolveType(IRubySearchScope scope) throws RubyModelException {
+ IRubyElement elem = getContainer(scope);
+ if (elem instanceof IRubyScript)
+ return RubyModelUtil.findTypeInRubyScript((IRubyScript)elem, getTypeQualifiedName());
+ return null;
+ }
+
+ protected boolean doEquals(TypeInfo other) {
+ // Don't compare the modifiers since they aren't relevant to identify
+ // a type.
+ return fName.equals(other.fName) && fPackage.equals(other.fPackage)
+ && CharOperation.equals(fEnclosingNames, other.fEnclosingNames);
+ }
+
+...
[truncated message content] |