1 package net.sf.jabref.gui;
3 import net.sf.jabref.Globals;
5 import javax.swing.table.AbstractTableModel;
6 import javax.swing.event.TableModelEvent;
8 import java.util.ArrayList;
9 import java.util.Iterator;
12 * Data structure to contain a list of file links, parseable from a coded string.
13 * Doubles as a table model for the file list editor.
15 public class FileListTableModel extends AbstractTableModel {
17 private final ArrayList list = new ArrayList();
19 public FileListTableModel() {
22 public int getRowCount() {
28 public int getColumnCount() {
32 public Class getColumnClass(int columnIndex) {
36 public Object getValueAt(int rowIndex, int columnIndex) {
38 FileListEntry entry = (FileListEntry)list.get(rowIndex);
39 switch (columnIndex) {
40 case 0: return entry.getDescription();
41 case 1: return entry.getLink();
42 default: return entry.getType() != null ?
43 entry.getType().getName() : "";
48 public FileListEntry getEntry(int index) {
50 return (FileListEntry)list.get(index);
54 public void removeEntry(int index) {
57 fireTableRowsDeleted(index, index);
63 * Add an entry to the table model, and fire a change event. The change event
64 * is fired on the event dispatch thread.
65 * @param index The row index to insert the entry at.
66 * @param entry The entry to insert.
68 public void addEntry(final int index, final FileListEntry entry) {
70 list.add(index, entry);
71 if (!SwingUtilities.isEventDispatchThread()) {
72 SwingUtilities.invokeLater(new Runnable() {
74 fireTableRowsInserted(index, index);
78 fireTableRowsInserted(index, index);
83 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
87 * Set up the table contents based on the flat string representation of the file list
88 * @param value The string representation
90 public void setContent(String value) {
91 setContent(value, false);
94 private FileListEntry setContent(String value, boolean firstOnly) {
97 ArrayList newList = new ArrayList();
98 StringBuilder sb = new StringBuilder();
99 ArrayList thisEntry = new ArrayList();
100 boolean inXmlChar = false;
101 boolean escaped = false;
102 for (int i=0; i<value.length(); i++) {
103 char c = value.charAt(i);
104 if (!escaped && (c == '\\')) {
108 // Check if we are entering an XML special character construct such
109 // as ",", because we need to know in order to ignore the semicolon.
110 else if (!escaped && (c == '&') && !inXmlChar) {
112 if ((value.length() > i+1) && (value.charAt(i+1) == '#'))
115 // Check if we are exiting an XML special character construct:
116 else if (!escaped && inXmlChar && (c == ';')) {
120 else if (!escaped && (c == ':')) {
121 thisEntry.add(sb.toString());
122 sb = new StringBuilder();
124 else if (!escaped && (c == ';') && !inXmlChar) {
125 thisEntry.add(sb.toString());
126 sb = new StringBuilder();
128 return decodeEntry(thisEntry);
130 newList.add(decodeEntry(thisEntry));
138 thisEntry.add(sb.toString());
139 if (thisEntry.size() > 0) {
141 return decodeEntry(thisEntry);
143 newList.add(decodeEntry(thisEntry));
146 synchronized (list) {
148 list.addAll(newList);
150 fireTableChanged(new TableModelEvent(this));
156 * Convenience method for finding a label corresponding to the type of the
157 * first file link in the given field content. The difference between using
158 * this method and using setContent() on an instance of FileListTableModel
159 * is a slight optimization: with this method, parsing is discontinued after
160 * the first entry has been found.
161 * @param content The file field content, as fed to this class' setContent() method.
162 * @return A JLabel set up with no text and the icon of the first entry's file type,
163 * or null if no entry was found.
165 public static JLabel getFirstLabel(String content) {
166 FileListTableModel tm = new FileListTableModel();
167 FileListEntry entry = tm.setContent(content, true);
168 return entry != null ? entry.getType().getIconLabel() : null;
172 private FileListEntry decodeEntry(ArrayList contents) {
173 return new FileListEntry(getElementIfAvailable(contents, 0),
174 getElementIfAvailable(contents, 1),
175 Globals.prefs.getExternalFileTypeByName
176 (getElementIfAvailable(contents, 2)));
179 private String getElementIfAvailable(ArrayList contents, int index) {
180 if (index < contents.size())
181 return (String)contents.get(index);
186 * Transform the file list shown in the table into a flat string representable
188 * @return String representation.
190 public String getStringRepresentation() {
191 StringBuilder sb = new StringBuilder();
192 for (Iterator iterator = list.iterator(); iterator.hasNext();) {
193 FileListEntry entry = (FileListEntry) iterator.next();
194 sb.append(encodeEntry(entry));
195 if (iterator.hasNext())
198 return sb.toString();
202 * Transform the file list shown in the table into a HTML string representation
203 * suitable for displaying the contents in a tooltip.
204 * @return Tooltip representation.
206 public String getToolTipHTMLRepresentation() {
207 StringBuilder sb = new StringBuilder("<html>");
208 for (Iterator iterator = list.iterator(); iterator.hasNext();) {
209 FileListEntry entry = (FileListEntry) iterator.next();
210 sb.append(entry.getDescription()).append(" (").append(entry.getLink()).append(')');
211 if (iterator.hasNext())
214 return sb.append("</html>").toString();
217 private String encodeEntry(FileListEntry entry) {
218 StringBuilder sb = new StringBuilder();
219 sb.append(encodeString(entry.getDescription()));
221 sb.append(encodeString(entry.getLink()));
223 sb.append(encodeString(entry.getType() != null ? entry.getType().getName() : ""));
224 return sb.toString();
227 private String encodeString(String s) {
228 StringBuilder sb = new StringBuilder();
229 for (int i=0; i<s.length(); i++) {
230 char c = s.charAt(i);
231 if ((c == ';') || (c == ':') || (c == '\\'))
235 return sb.toString();
238 public void print() {
239 System.out.println("----");
240 for (Iterator iterator = list.iterator(); iterator.hasNext();) {
241 FileListEntry fileListEntry = (FileListEntry) iterator.next();
242 System.out.println(fileListEntry);
244 System.out.println("----");