📄 codetemplatemanager.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 + -