/* * 02/21/2005 * * CodeTemplate.java - A "template" (macro) for commonly-typed code. * * This library is distributed under a modified BSD license. See the included * RSyntaxTextArea.License.txt file for details. */ package org.fife.ui.rsyntaxtextarea.templates; import java.io.IOException; import java.io.ObjectInputStream; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.Element; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; /** * A code template that inserts static text before and after the caret.<p> * * For example, you can associate the identifier <code>forb</code> * (short for "for-block") with the following code:<p> * * <pre> * for (<caret>) { * * } * </pre> * * Then, whenever you type <code>forb</code> followed by a trigger * (e.g., a space) into a text area with this <code>CodeTemplate</code>, * the code snippet is added in place of <code>forb</code>. Further, * the caret is placed at the position denoted by <code><caret></code>. * * @author Robert Futrell * @version 0.1 * @see CodeTemplate */ public class StaticCodeTemplate extends AbstractCodeTemplate { private static final long serialVersionUID = 1; /** * The code inserted before the caret position. */ private String beforeCaret; /** * The code inserted after the caret position. */ private String afterCaret; /** * Cached value representing whether <code>beforeCaret</code> contains * one or more newlines. */ private transient int firstBeforeNewline; /** * Cached value representing whether <code>afterCaret</code> contains * one or more newlines. */ private transient int firstAfterNewline; private static final String EMPTY_STRING = ""; /** * Constructor. This constructor only exists to support persistance * through serialization. */ public StaticCodeTemplate() { } /** * Constructor. * * @param id The ID of this code template. * @param beforeCaret The text to place before the caret. * @param afterCaret The text to place after the caret. */ public StaticCodeTemplate(String id, String beforeCaret, String afterCaret){ super(id); setBeforeCaretText(beforeCaret); setAfterCaretText(afterCaret); } /** * Returns the text that will be placed after the caret. * * @return The text. * @see #setAfterCaretText */ public String getAfterCaretText() { return afterCaret; } /** * Returns the text that will be placed before the caret. * * @return The text. * @see #setBeforeCaretText */ public String getBeforeCaretText() { return beforeCaret; } /** * Returns the "after caret" text, with each new line indented by * the specified amount. * * @param indent The amount to indent. * @return The "after caret" text. */ private String getAfterTextIndented(String indent) { return getTextIndented(getAfterCaretText(), firstAfterNewline, indent); } /** * Returns the "before caret" text, with each new line indented by * the specified amount. * * @param indent The amount to indent. * @return The "before caret" text. */ private String getBeforeTextIndented(String indent) { return getTextIndented(getBeforeCaretText(),firstBeforeNewline,indent); } /** * Returns text with newlines indented by the specified amount. * * @param text The original text. * @param firstNewline The index of the first '\n' character. * @param indent The amount to indent. * @return The indented text. */ private String getTextIndented(String text,int firstNewline,String indent) { if (firstNewline==-1) { return text; } int pos = 0; int old = firstNewline+1; StringBuilder sb = new StringBuilder(text.substring(0, old)); sb.append(indent); while ((pos=text.indexOf('\n', old))>-1) { sb.append(text.substring(old, pos+1)); sb.append(indent); old = pos+1; } if (old<text.length()) { sb.append(text.substring(old)); } return sb.toString(); } /** * Invokes this code template. The changes are made to the given text * area. * * @param textArea The text area to operate on. * @throws BadLocationException If something bad happens. */ public void invoke(RSyntaxTextArea textArea) throws BadLocationException { Caret c = textArea.getCaret(); int dot = c.getDot(); int mark = c.getMark(); int p0 = Math.min(dot, mark); int p1 = Math.max(dot, mark); RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument(); Element map = doc.getDefaultRootElement(); int lineNum = map.getElementIndex(dot); Element line = map.getElement(lineNum); int start = line.getStartOffset(); int end = line.getEndOffset()-1; // Why always "-1"? String s = textArea.getText(start,end-start); int len = s.length(); // endWS is the end of the leading whitespace // of the current line. int endWS = 0; while (endWS<len && RSyntaxUtilities.isWhitespace(s.charAt(endWS))) { endWS++; } s = s.substring(0, endWS); p0 -= getID().length(); String beforeText = getBeforeTextIndented(s); String afterText = getAfterTextIndented(s); doc.replace(p0,p1-p0, beforeText+afterText, null); textArea.setCaretPosition(p0+beforeText.length()); } /** * Called when reading a serialized version of this document. This is * overridden to initialize the transient members of this class. * * @param in The input stream to read from. * @throws ClassNotFoundException Never. * @throws IOException If an IO error occurs. */ private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); // "Resetting" before and after text to the same values will replace // nulls with empty srings, and set transient "first*Newline" values. setBeforeCaretText(this.beforeCaret); setAfterCaretText(this.afterCaret); } /** * Sets the text to place after the caret. * * @param afterCaret The text. * @see #getAfterCaretText() */ public void setAfterCaretText(String afterCaret) { this.afterCaret = afterCaret==null ? EMPTY_STRING : afterCaret; firstAfterNewline = this.afterCaret.indexOf('\n'); } /** * Sets the text to place before the caret. * * @param beforeCaret The text. * @see #getBeforeCaretText() */ public void setBeforeCaretText(String beforeCaret) { this.beforeCaret = beforeCaret==null ? EMPTY_STRING : beforeCaret; firstBeforeNewline = this.beforeCaret.indexOf('\n'); } /** * Returns a string representation of this template for debugging * purposes. * * @return A string representation of this template. */ @Override public String toString() { return "[StaticCodeTemplate: id=" + getID() + ", text=" + getBeforeCaretText() + "|" + getAfterCaretText() + "]"; } }