/* * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 1999, 2004 Slava Pestov * * 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.gjt.sp.jedit; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; /** Class used by PerspectiveManager to parse SplitConfig strings. May also be used by plugins. @since jEdit 4.4 */ public class SplitConfigParser { //{{{ private members private String splitConfig; private boolean includeSplits = true; private boolean includeFiles = true; private boolean includeRemotes = false; //}}} //{{{ SplitConfigParser constructor /** * @param splitConfig The string to parse and adjust. */ public SplitConfigParser(String splitConfig) { this.splitConfig = splitConfig == null ? "" : splitConfig; } //}}} //{{{ Setters /** * @param b If true, retain any splits in the split configuration. */ public void setIncludeSplits(boolean b) { includeSplits = b; } /** * @param b If true, retain any file names found in the split configuration. */ public void setIncludeFiles(boolean b) { includeFiles = b; } /** * @param b If true, and if include files is true, then retain any remote file * names found in the split configuration. */ public void setIncludeRemoteFiles(boolean b) { includeRemotes = includeFiles && b; } //}}} //{{{ parse() /** * Parses the given split configuration string and removes splits, file names, * and remote file names bases on the settings for this parser. * @return The split configuration string adjusted for user preferences. */ public String parse() { if (splitConfig == null || splitConfig.length() == 0) { return ""; } Deque<Object> tokenStack = new ArrayDeque<Object>(); Deque<Object> splitStack = new ArrayDeque<Object>(); BufferSet bufferset = new BufferSet(includeFiles, includeRemotes); boolean haveSplit = false; try { StreamTokenizer st = new StreamTokenizer(new StringReader(splitConfig)); st.whitespaceChars(0, ' '); st.wordChars('#', '~'); st.commentChar('!'); st.quoteChar('"'); st.eolIsSignificant(false); int token = st.nextToken(); while (token != StreamTokenizer.TT_EOF) { switch (token) { case StreamTokenizer.TT_WORD: if ("vertical".equals(st.sval) || "horizontal".equals(st.sval)) { // handle split -- create new Split, populate it with // the first 2 items in the split stack. if (includeSplits) { Object right = splitStack.pop(); Object left = splitStack.pop(); Split split = new Split(); split.setLeft(left); split.setRight(right); split.setDirection(st.sval); int offset = (Integer) tokenStack.pop(); split.setOffset(offset); splitStack.push(split); haveSplit = true; } } else if ("buffer".equals(st.sval) || "buff".equals(st.sval)) { // add to buffer set Object filename = tokenStack.pop(); bufferset.addBuffer(filename.toString()); } else if ("bufferset".equals(st.sval)) { // close out current buffer set, push to split stack, // create new buffer set. Object scope = tokenStack.pop(); bufferset.setScope(scope.toString()); splitStack.push(bufferset); bufferset = new BufferSet(includeFiles, includeRemotes); } break; case StreamTokenizer.TT_NUMBER: tokenStack.push((int) st.nval); break; case '"': tokenStack.push(st.sval); break; } token = st.nextToken(); } StringBuilder sb = new StringBuilder(); // check if splitStack has any Split objects, if not, collapse all // BufferSets to a single BufferSet. if (haveSplit) { while (!splitStack.isEmpty()) { sb.append(splitStack.pop().toString()).append(' '); } } else { // no splits, only buffersets BufferSet allBuffers = new BufferSet(); while (!splitStack.isEmpty()) { BufferSet bs = (BufferSet) splitStack.pop(); if (allBuffers.getScope() == null) { allBuffers.setScope(bs.getScope()); } allBuffers.addBufferSet(bs); } sb.append(allBuffers.toString()); } // need the replaceAll to make sure Windows backslashes // don't get unescaped prematurely return sb.toString().replaceAll("\\\\", "\\\\\\\\").trim(); } catch (IOException e) { // StringReader will not throw an IOException as long as the // string it is reading is not null, which won't happen here. } return splitConfig; } //}}} //{{{ BufferSet // Represents a set of file names for buffers. private class BufferSet { List<String> buffers = new ArrayList<String>(); String scope = null; boolean includeFiles = true; boolean includeRemotes = false; public BufferSet() {} public BufferSet(boolean includeFiles, boolean includeRemotes) { this.includeFiles = includeFiles; this.includeRemotes = includeRemotes; } public void addBuffer(String s) { if (includeFiles) { if (includeRemotes) { buffers.add(s); return; } if (!isRemote(s)) { buffers.add(s); } } } public List<String> getBuffers() { return buffers; } public void addBufferSet(BufferSet bs) { buffers.addAll(bs.getBuffers()); } public void setScope(String s) { scope = s; } public String getScope() { return scope; } public String toString() { StringBuilder sb = new StringBuilder(); if (buffers.size() == 0) { sb.append("\"Untitled-1\" buffer "); } else { for (int i = 0; i < buffers.size(); i++) { sb.append('\"').append(buffers.get(i)).append('\"'); sb.append(i == 0 ? " buffer " : " buff "); } } if (scope == null) { scope = "view"; } sb.append('\"').append(scope).append("\" bufferset"); return sb.toString(); } /** * @return true if the uri points to a file that is remote, that is, the * protocol of the give uri is something other than 'file'. */ public boolean isRemote(String uri) { if (MiscUtilities.isURL(uri)) { String protocol = MiscUtilities.getProtocolOfURL(uri); return !protocol.equals("file"); } return false; } } //}}} //{{{ Split private class Split { Object left = null; Object right = null; String direction = null; int offset = 0; // no error checking, assumes caller will pass a BufferSet or a Split public void setLeft(Object left) { this.left = left; } // no error checking, assumes caller will pass a BufferSet or a Split public void setRight(Object right) { this.right = right; } // no error checking, assumes caller will send 'horizontal' or 'vertical' public void setDirection(String direction) { this.direction = direction; } // no error checking, assumes caller will send offset >= 0 public void setOffset(int offset) { this.offset = offset; } public String toString() { StringBuilder sb = new StringBuilder(); if (left != null) { sb.append(left.toString()).append(' '); } if (right != null) { sb.append(right.toString()).append(' '); } sb.append(offset).append(' ').append(direction); return sb.toString(); } } //}}} }