49a66435b19efc68d73422f47acebe3b92d659be
[debian/jabref.git] / src / java / net / sf / jabref / imports / FieldContentParser.java
1 /*  Copyright (C) 2003-2011 JabRef contributors.
2     This program is free software; you can redistribute it and/or modify
3     it under the terms of the GNU General Public License as published by
4     the Free Software Foundation; either version 2 of the License, or
5     (at your option) any later version.
6
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     GNU General Public License for more details.
11
12     You should have received a copy of the GNU General Public License along
13     with this program; if not, write to the Free Software Foundation, Inc.,
14     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15 */
16 package net.sf.jabref.imports;
17
18 import net.sf.jabref.Globals;
19 import net.sf.jabref.GUIGlobals;
20
21
22 /**
23  * This class provides the reformatting needed when reading BibTeX fields formatted
24  * in JabRef style. The reformatting must undo all formatting done by JabRef when
25  * writing the same fields.
26  */
27 public class FieldContentParser {
28
29     /**
30      * Performs the reformatting
31      * @param content StringBuffer containing the field to format. key contains field name according to field
32      *  was edited by Kuehn/Havalevich
33      * @return The formatted field content. NOTE: the StringBuffer returned may
34      * or may not be the same as the argument given.
35      */
36         public StringBuffer format(StringBuffer content, String key) {
37
38         /*System.out.println("Content: '"+content+"'");
39         byte[] bt = content.toString().getBytes();
40         for (int i = 0; i < bt.length; i++) {
41             byte b = bt[i];
42             System.out.print(b+" ");
43         }
44         System.out.println("");
45         */
46         //boolean rep = false;
47
48         int i=0;
49
50         // Remove windows newlines and insert unix ones:
51         // TODO: 2005.12.3: Added replace from \r to \n, to work around a reported problem of words stiched together.
52         // But: we need to find out why these lone \r characters appear in his file.
53         content = new StringBuffer(content.toString().replaceAll("\r\n","\n").replaceAll("\r", "\n"));
54
55         while (i<content.length()) {
56
57             int c = content.charAt(i);
58             if (c == '\n') {
59                 if ((content.length()>i+1) && (content.charAt(i+1)=='\t')
60                     && ((content.length()==i+2) || !Character.isWhitespace(content.charAt(i+2)))) {
61                     // We have either \n\t followed by non-whitespace, or \n\t at the
62                     // end. Bothe cases indicate a wrap made by JabRef. Remove and insert space if necessary.
63
64                     content.deleteCharAt(i); // \n
65                     content.deleteCharAt(i); // \t
66                     // Add space only if necessary:
67                     // Note 2007-05-26, mortenalver: the following line was modified. It previously
68                     // didn't add a space if the line break was at i==0. This caused some occurences of
69                     // "string1 # { and } # string2" constructs lose the space in front of the "and" because
70                     // the line wrap caused a JabRef linke break at the start of a value containing the " and ".
71                     // The bug was caused by a protective check for i>0 to avoid intexing char -1 in content.
72                     if ((i==0) || !Character.isWhitespace(content.charAt(i-1))) {
73                         content.insert(i, ' ');
74                         // Increment i because of the inserted character:
75                         i++;
76                     }
77                 }
78                 else if ((content.length()>i+3) && (content.charAt(i+1)=='\t')
79                     && (content.charAt(i+2)==' ')
80                     && !Character.isWhitespace(content.charAt(i+3))) {
81                     // We have \n\t followed by ' ' followed by non-whitespace, which indicates
82                     // a wrap made by JabRef <= 1.7.1. Remove:
83                     content.deleteCharAt(i); // \n
84                     content.deleteCharAt(i); // \t
85                     // Remove space only if necessary:
86                     if ((i>0) && Character.isWhitespace(content.charAt(i-1))) {
87                         content.deleteCharAt(i);
88                     }
89                 }
90                 else if ((content.length()>i+3) && (content.charAt(i+1)=='\t')
91                         && (content.charAt(i+2)=='\n') && (content.charAt(i+3)=='\t')) {
92                     // We have \n\t\n\t, which looks like a JabRef-formatted empty line.
93                     // Remove the tabs and keep one of the line breaks:
94                     content.deleteCharAt(i+1); // \t
95                     content.deleteCharAt(i+1); // \n
96                     content.deleteCharAt(i+1); // \t
97                     // Skip past the line breaks:
98                     i++;
99
100                     // Now, if more \n\t pairs are following, keep each line break. This
101                     // preserves several line breaks properly. Repeat until done:
102                     while ((content.length()>i+1) && (content.charAt(i)=='\n')
103                         && (content.charAt(i+1)=='\t')) {
104
105                         content.deleteCharAt(i+1);
106                         i++;
107                     }
108                 }
109                 else if ((content.length()>i+1) && (content.charAt(i+1)!='\n')) {
110                     // We have a line break not followed by another line break. This is probably a normal
111                     // line break made by whatever other editor, so we will remove the line break.
112                     content.deleteCharAt(i);
113                     // If the line break is not accompanied by other whitespace we must add a space:
114                     if (!Character.isWhitespace(content.charAt(i)) &&  // No whitespace after?
115                             (i>0) && !Character.isWhitespace(content.charAt(i-1))) // No whitespace before?
116                         content.insert(i, ' ');
117                 }
118
119                 //else if ((content.length()>i+1) && (content.charAt(i+1)=='\n'))
120                 else
121                     i++;
122                 //content.deleteCharAt(i);
123             }
124             else if (c == ' ') {
125                 //if ((content.length()>i+2) && (content.charAt(i+1)==' ')) {
126                 if ((i>0) && (content.charAt(i-1)==' ')) {
127                     // We have two spaces in a row. Don't include this one.
128                         
129                         // Yes, of course we have, but in Filenames it is nessary to have all spaces. :-)
130                         // This is the reason why the next lines are required
131                         if(key != null && key.equals(GUIGlobals.FILE_FIELD)){
132                                 i++;
133                         }
134                         else
135                                 content.deleteCharAt(i);
136                 }
137                 else
138                     i++;
139             } else if (c == '\t')
140                 // Remove all tab characters that aren't associated with a line break.
141                 content.deleteCharAt(i);
142             else
143                 i++;
144
145         }
146         
147         return content;
148         }
149
150     /**
151      * Performs the reformatting
152      * @param content StringBuffer containing the field to format.
153      * @return The formatted field content. NOTE: the StringBuffer returned may
154      * or may not be the same as the argument given.
155      */
156     public StringBuffer format(StringBuffer content) { 
157         return format(content, null);
158     }
159
160     /**
161      * Formats field contents for output. Must be "symmetric" with the parse method above,
162      * so stored and reloaded fields are not mangled.
163      * @param in
164      * @param wrapAmount
165      * @return the wrapped String.
166      */
167     public static String wrap(String in, int wrapAmount){
168         
169         String[] lines = in.split("\n");
170         StringBuffer res = new StringBuffer();
171         addWrappedLine(res, lines[0], wrapAmount);
172         for (int i=1; i<lines.length; i++) {
173
174             if (!lines[i].trim().equals("")) {
175                 res.append(Globals.NEWLINE);
176                 res.append('\t');
177                 res.append(Globals.NEWLINE);
178                 res.append('\t');
179                 addWrappedLine(res, lines[i], wrapAmount);
180             } else {
181                 res.append(Globals.NEWLINE);
182                 res.append('\t');
183             }
184         }
185         return res.toString();
186     }
187
188     private static void addWrappedLine(StringBuffer res, String line, int wrapAmount) {
189         // Set our pointer to the beginning of the new line in the StringBuffer:
190         int p = res.length();
191         // Add the line, unmodified:
192         res.append(line);
193
194         while (p < res.length()){
195             int q = res.indexOf(" ", p+wrapAmount);
196             if ((q < 0) || (q >= res.length()))
197                 break;
198
199             res.deleteCharAt(q);
200             res.insert(q, Globals.NEWLINE+"\t");
201             p = q+Globals.NEWLINE_LENGTH;
202
203         }
204     }
205
206     static class Indents {
207         //int hyp
208     }
209 }