All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.rsta.ac.java.PackageMapNode Maven / Gradle / Ivy

Go to download

A library adding code completion and other advanced features for Java, JavaScript, Perl, and other languages to RSyntaxTextArea.

There is a newer version: 3.3.0
Show newest version
/*
 * 04/04/2015
 *
 * Copyright (C) 2015 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.java;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.fife.rsta.ac.java.buildpath.LibraryInfo;
import org.fife.rsta.ac.java.classreader.ClassFile;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;


/**
 * A data structure modeling all classes in a jar or directory, or on a
 * classpath.  It's a recursive mapping of Strings to either
 * Maps or {@link ClassFile}s (which are lazily created and
 * may be null).  At each level of the nested map, the string
 * key is a package name iff its corresponding value is a Map.
 * Examine that Map's contents to explore the contents of
 * that package.  If the corresponding value is a ClassFile,
 * then the string key's value is the name of that class.  Finally, if
 * the corresponding value is null, then the string key's
 * value is the name of a class, but its contents have not yet been
 * loaded for use by the code completion library (ClassFiles
 * are lazily loaded to conserve memory).
 */
public class PackageMapNode {

	/**
	 * A mapping of sub-package name to the sub-packages and classes under it.
	 */
	private SortedMap subpackages;

	/**
	 * A mapping of class file names to class files in this package.  The
	 * actual {@link ClassFile} values are lazily instantiated, so any map
	 * entry with a value of null simply has not been created
	 * yet.
	 */
	private SortedMap classFiles;


	public PackageMapNode() {
		subpackages =
			new TreeMap(String.CASE_INSENSITIVE_ORDER);
		classFiles =
			new TreeMap(String.CASE_INSENSITIVE_ORDER);
	}


	/**
	 * Adds entries for a fully-qualified class name, of the form
	 * "org/fife/util/DynamicIntArray.class".  This method should
	 * only be called on the "root" node of a package map.
	 *
	 * @param className A fully-qualified class name of the form described
	 *        above.
	 */
	public void add(String className) {

		String[] tokens = Util.splitOnChar(className, '/');
		PackageMapNode pmn = this;

		for (int i=0; i.' character.  This should be (the start of) a
	 *        fully-qualified class, interface, or enum name.
	 * @param addTo The list to add completion choices to.
	 */
	public void addCompletions(LibraryInfo info, CompletionProvider provider,
			String[] pkgNames, Set addTo) {

		PackageMapNode map = this;
		for (int i=0; i largest valid class char

		// First add completions for matching subpackage names.
		SortedMap subpackages = map.subpackages.subMap(fromKey, toKey);
		if (!subpackages.isEmpty()) {

			StringBuilder sb = new StringBuilder();
			for (int j=0; j entry : subpackages.entrySet()) {
				String completionPackageName = entry.getKey();
				String text = earlierPackages + completionPackageName;
				addTo.add(new PackageNameCompletion(provider, text, fromKey));
			}

		}

		// Next, add completions for matching class names
		SortedMap sm = map.classFiles.subMap(fromKey, toKey);
		for (Map.Entry entry : sm.entrySet()) {

			String key = entry.getKey();
			ClassFile cf = entry.getValue();

			// The ClassFile may have already been loaded for this one
			if (cf != null) {
				boolean inPkg = false; // TODO: Pass me in
				if (inPkg || org.fife.rsta.ac.java.classreader.Util.isPublic(cf.getAccessFlags())) {
					addTo.add(new ClassCompletion(provider, cf));
				}
			}

			// If the ClassFile isn't yet cached
			else {
				String[] items = new String[pkgNames.length];
				System.arraycopy(pkgNames, 0, items, 0, pkgNames.length-1);
				items[items.length-1] = key;
				cf = getClassEntry(info, items);
				if (cf!=null) {
					boolean inPkg = false; // TODO: Pass me in
					if (inPkg || org.fife.rsta.ac.java.classreader.Util.isPublic(cf.getAccessFlags())) {
						addTo.add(new ClassCompletion(provider, cf));
					}
				}
				else {
					// This should never happen - class name without a ClassFile
				}
			}

		}

	}


	/**
	 * Removes the cache of all ClassFiles from this package
	 * map.
	 *
	 * @return The number of class file entries removed.
	 */
	public int clearClassFiles() {
		return clearClassFilesImpl(this);
	}


	private int clearClassFilesImpl(PackageMapNode pmn) {

		int clearedCount = 0;

		for (Map.Entry entry : pmn.classFiles.entrySet()) {
			entry.setValue(null);
			clearedCount++;
		}

		for (Map.Entry entry : pmn.subpackages.entrySet()) {
			clearedCount += clearClassFilesImpl(entry.getValue());
		}

		return clearedCount;

	}


	public boolean containsClass(String className) {

		String[] items = className.split("\\.");

		PackageMapNode pmn = this;
		for (int i=0; i addTo,
			String[] pkgs, boolean inPkg) {

		PackageMapNode map = this;

		for (int i=0; i entry : map.classFiles.entrySet()) {

					ClassFile cf = entry.getValue();
					if (cf == null) {
						StringBuilder name = new StringBuilder(pkgs[0]);
						for (int j=1; jmap belongs to (i.e.
	 *        all levels of packages scanned before this one), separated by
	 *        '/'.
	 * @param addTo The list to add any matching ClassFiles to.
	 */
	void getClassesWithNamesStartingWith(LibraryInfo info, String prefix,
			String currentPkg, List addTo) {

		final int prefixLen = prefix.length();

		for (Map.Entry children : subpackages.entrySet()) {
			String key = children.getKey();
			PackageMapNode child = children.getValue();
			child.getClassesWithNamesStartingWith(info, prefix,
					currentPkg + key + "/", addTo);
		}

		for (Map.Entry cfEntry : classFiles.entrySet()) {
			// If value is null, we only lazily create the ClassFile if
			// necessary (i.e. if the class name does match what they've
			// typed).
			String className = cfEntry.getKey();
			if (className.regionMatches(true, 0, prefix, 0, prefixLen)) {
				ClassFile cf = cfEntry.getValue();
				if (cf==null) {
					String fqClassName = currentPkg + className + ".class";
					try {
						cf = info.createClassFile(fqClassName);
						cfEntry.setValue(cf); // Update the map
					} catch (IOException ioe) {
						ioe.printStackTrace();
					}
				}
				if (cf!=null) { // possibly null if IOException above
					addTo.add(cf);
				}
			}
		}

	}


	private static final void possiblyAddTo(Collection addTo,
			ClassFile cf, boolean inPkg) {
		if (inPkg || org.fife.rsta.ac.java.classreader.Util.isPublic(cf.getAccessFlags())) {
			addTo.add(cf);
		}
	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy