⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 codetemplatemanager.java

📁 具有不同语法高亮的编辑器实例
💻 JAVA
字号:
/*
 * 02/21/2005
 *
 * CodeTemplateManager.java - manages code templates.
 * Copyright (C) 2005 Robert Futrell
 * email@address.com
 * www.website.com
 *
 * This program 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 any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.fife.ui.rsyntaxtextarea;

import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;


/**
 * Manages "code templates."<p>
 *
 * Note that this class assumes that thread-safety is taken care of at a
 * higher level (which should be the case, as <code>AbstractDocument</code>s
 * only allow text insertions during a write-lock).  You should never have
 * to use this class directly anyway; its only client should be
 * <code>RSyntaxTextArea</code>.
 *
 * @author Robert Futrell
 * @version 0.1
 */
class CodeTemplateManager {

	private int maxTemplateIDLength;
	private CodeTemplate[] templates;

	private KeyStroke insertTrigger;
	private String insertTriggerString;
	private Segment s;
	private TemplateComparator comparator;
	private File directory;


/*****************************************************************************/


	/**
	 * Constructor.
	 */
	public CodeTemplateManager() {

		// Default insert trigger is a space.
		// FIXME:  See notes in RSyntaxTextAreaDefaultInputMap.
		// Be aware that you may need to use getKeyStroke(' ') when you
		// fix this...
		setInsertTrigger(KeyStroke.getKeyStroke(' '));

		s = new Segment();
		comparator = new TemplateComparator();

	}


/*****************************************************************************/


	/**
	 * Returns the keystroke that is the "insert trigger" for templates;
	 * that is, the character that, when inserted into an instance of
	 * <code>RSyntaxTextArea</code>, triggers the search for
	 * a template matching the token ending at the caret position.
	 *
	 * @return The insert trigger.
	 * @see #getInsertTriggerString
	 * @see #setInsertTrigger
	 */
	/*
	 * FIXME:  This text IS what's inserted if the trigger character is pressed
	 * in a text area but no template matches, but it is NOT the trigger
	 * character used in the text areas.  This is because space (" ") is
	 * hard-coded into RSyntaxTextAreaDefaultInputMap.java.  We need to make
	 * this dynamic somehow.  See RSyntaxTextAreaDefaultInputMap.java.
	 */
	public KeyStroke getInsertTrigger() {
		return insertTrigger;
	}


/*****************************************************************************/


	/**
	 * Returns the "insert trigger" for templates; that is, the character
	 * that, when inserted into an instance of <code>RSyntaxTextArea</code>,
	 * triggers the search for a template matching the token ending at the
	 * caret position.
	 *
	 * @return The insert trigger character.
	 * @see #getInsertTrigger
	 * @see #setInsertTrigger
	 */
	/*
	 * FIXME:  This text IS what's inserted if the trigger character is pressed
	 * in a text area but no template matches, but it is NOT the trigger
	 * character used in the text areas.  This is because space (" ") is
	 * hard-coded into RSyntaxTextAreaDefaultInputMap.java.  We need to make
	 * this dynamic somehow.  See RSyntaxTextAreaDefaultInputMap.java.
	 */
	public String getInsertTriggerString() {
		return insertTriggerString;
	}


/*****************************************************************************/


	/**
	 * Returns the template that should be inserted at the current caret
	 * position, assuming the trigger character was pressed.
	 *
	 * @param textArea The text area that's getting text inserted into
	 *                 it.
	 * @return A template that should be inserted, if appropriate,
	 *         or <code>null</code> if no template should be
	 *         inserted.
	 */
	public CodeTemplate getTemplate(RSyntaxTextArea textArea) {
		int caretPos = textArea.getCaretPosition();
		int charsToGet = Math.min(caretPos, maxTemplateIDLength);
		try {
			Document doc = textArea.getDocument();
			doc.getText(caretPos-charsToGet, charsToGet, s);
			int index = Arrays.binarySearch(templates, s, comparator);
			if (index>=0)
				return templates[index];
			return null;
		} catch (BadLocationException ble) {
			ble.printStackTrace();
			throw new InternalError("Error in CodeTemplateManager");
		}
	}


/*****************************************************************************/


	/**
	 * Returns the number of templates this manager knows about.
	 *
	 * @return The template count.
	 */
	public int getTemplateCount() {
		return templates!=null ? templates.length : 0;
	}


/*****************************************************************************/


	/**
	 * Returns the templates currently available.  This method exists
	 * solely so <code>TemplateOptionPanel</code> can allow users
	 * to modify/add/remove templates.  You should never call this
	 * method.
	 *
	 * @return The templates available.
	 */
	CodeTemplate[] getTemplates() {
		return templates;
	}


/*****************************************************************************/


	/**
	 * Replaces the current set of available templates with the ones
	 * specified.  This method exists
	 * solely so <code>TemplateOptionPanel</code> can allow users
	 * to modify/add/remove templates.  You should never call this
	 * method.
	 *
	 * @param newTemplates The new set of templates.  Note that
	 *                     we will be taking a shallow copy of these and
	 *                     sorting them.
	 */
	void replaceTemplates(CodeTemplate[] newTemplates) {
		templates = newTemplates;
		sortTemplates(); // Also recomputes maxTemplateIDLength.
	}


/*****************************************************************************/


	/**
	 * Saves all templates as XML files in the current template directory.
	 *
	 * @return Whether or not the save was successful.
	 */
	public boolean saveTemplates() {

		if (templates==null)
			return true;
		if (!directory.isDirectory())
			return false;

		// Blow away all old XML files to start anew, as some might be from
		// templates we're removed from the template manager.
		File[] oldXMLFiles = directory.listFiles(new XMLFileFilter());
		if (oldXMLFiles==null)
			return false; // Either an IOException or it isn't a directory.
		int count = oldXMLFiles.length;
		for (int i=0; i<count; i++) {
			/*boolean deleted = */oldXMLFiles[i].delete();
		}

		// Save all current templates as XML.
		boolean wasSuccessful = true;
		count = templates.length;
		for (int i=0; i<count; i++) {
			File xmlFile = new File(directory,
						new String(templates[i].getID()) + ".xml");
			try {
				if (!templates[i].saveToFile(xmlFile))
					wasSuccessful = false;
			} catch (Exception e) {
				wasSuccessful = false;
			}
		}

		return wasSuccessful;

	}


/*****************************************************************************/


	/**
	 * Sets the "trigger" character for templates.
	 *
	 * @param trigger The trigger character to set for templates.  This means
	 *                that when this character is pressed in an
	 *                <code>RSyntaxTextArea</code>,  the last-typed token is
	 *                found, and is checked against all template ID's to see if
	 *                a template should be inserted.  If a template ID matches,
	 *                that template is inserted; if not, the trigger character
	 *                is inserted.  If this parameter is <code>null</code>,
	 *                no change is made to the trigger character.
	 * @see #getInsertTrigger
	 * @see #getInsertTriggerString
	 */
	/*
	 * FIXME:  The trigger set here IS inserted when no matching template
	 * is found, but a space character (" ") is always used as the "trigger"
	 * to look for templates.  This is because it is hardcoded in
	 * RSyntaxTextArea's input map this way.  We need to change this.
	 * See RSyntaxTextAreaDefaultInputMap.java.
	 */
	public void setInsertTrigger(KeyStroke trigger) {
		if (trigger!=null) {
			insertTrigger = trigger;
			insertTriggerString = trigger.getKeyChar() + "";
		}
	}


/*****************************************************************************/


	/**
	 * Sets the directory in which to look for templates.  Calling this
	 * method causes all currently known templates to be discarded
	 * and new ones read (if any) from the specified directory.
	 *
	 * @param dir The new directory in which to look for templates.
	 * @return The number of templates loaded form the specified
	 *         directory, or <code>null</code> if the directory was invalid.
	 */
	public int setTemplateDirectory(File dir) {

		this.directory = dir;

		if (dir!=null && dir.isDirectory()) {
			File[] files = dir.listFiles(new XMLFileFilter());
			int count = files==null ? 0 : files.length;
			templates = new CodeTemplate[count];
			for (int i=0; i<count; i++) {
				templates[i] = CodeTemplate.loadFromFile(files[i]);
			}
			sortTemplates();
			return getTemplateCount();
		}

		templates = new CodeTemplate[0];
		return -1;

	}


/*****************************************************************************/


	/**
	 * Removes any null entries in the current set of templates (if
	 * any), sorts the remaining templates, and computes the new
	 * maximum template ID length.
	 */
	private final void sortTemplates() {

		// Remove any null entries (should only happen because of
		// IOExceptions, etc. when loading from files), and sort
		// the remaining list.
		int count = templates.length;
		for (int i=0; i<count; i++) {
			if (templates[i]==null) {
				CodeTemplate[] temp = new CodeTemplate[count-1];
				System.arraycopy(templates,0, temp,0, i);
				System.arraycopy(templates,i+1, temp,i, count-(i+1));
				templates = temp;
				count--;
				i = -1;
				continue;
			}
		}
		Arrays.sort(templates);

		// Get the maximum length of a template ID.
		maxTemplateIDLength = 0;
		count = templates.length;
		for (int i=0; i<count; i++) {
			maxTemplateIDLength = Math.max(maxTemplateIDLength,
								templates[i].getID().length);
		}

System.err.println("templates array length: " + templates.length);

	}


/*****************************************************************************/
/********************** INNER CLASSES ****************************************/
/*****************************************************************************/


	/**
	 * A comparator that takes a <code>CodeTemplate</code> as its first
	 * parameter and a <code>Segment</code> as its second, and knows
	 * to compare the template's ID to the segment's text.
	 */
	private class TemplateComparator implements Comparator {

		public TemplateComparator() {
		}

		public int compare(Object template, Object segment) {

			// Get template start index (0) and length.
			CodeTemplate t = (CodeTemplate)template;
			final char[] templateArray = t.getID();
			int i = 0;
			int len1 = templateArray.length;

			// Find "token" part of segment and get its offset and length.
			Segment s = (Segment)segment;
			char[] segArray = s.array;
			int len2 = s.count;
			int j = s.offset + len2 - 1;
			while (j>=s.offset && CodeTemplate.isValidChar(segArray[j]))
				j--;
			j++;
			int segShift = j - s.offset;
			len2 -= segShift;

			int n = Math.min(len1, len2);
			while (n-- != 0) {
				char c1 = templateArray[i++];
				char c2 = segArray[j++];
				if (c1 != c2)
					return c1 - c2;
			}
			return len1 - len2;

		}

		public boolean equals(Object obj) {
			return this.equals(obj);
		}

	}


/*****************************************************************************/


	/**
	 * A file filter for File.listFiles() (NOT for JFileChoosers!) that
	 * accepts only XML files.
	 */
	private static class XMLFileFilter implements FileFilter {
		public boolean accept(File f) {
			return f.getName().toLowerCase().endsWith(".xml");
		}
	}


/*****************************************************************************/

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -