1 |
/* |
2 |
openisis - an open implementation of the CDS/ISIS database |
3 |
Version 0.8.x (patchlevel see file Version) |
4 |
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
6 |
This library is free software; you can redistribute it and/or |
7 |
modify it under the terms of the GNU Lesser General Public |
8 |
License as published by the Free Software Foundation; either |
9 |
version 2.1 of the License, or (at your option) any later version. |
10 |
|
11 |
This library is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public |
17 |
License along with this library; if not, write to the Free Software |
18 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
see README for more information |
21 |
EOH */ |
22 |
|
23 |
|
24 |
package org.openisis; |
25 |
|
26 |
import java.util.StringTokenizer; // split |
27 |
|
28 |
|
29 |
/** |
30 |
Field implementation of openisis java binding. |
31 |
A Field is an immutable structure, binding a String value to a tag. |
32 |
It provides methods for formatted access to the value. |
33 |
<p> |
34 |
$Id: Field.java,v 1.5 2003/04/08 00:20:53 kripke Exp $ |
35 |
@version $Revision: 1.5 $ |
36 |
@author $Author: kripke $ |
37 |
*/ |
38 |
public class Field { |
39 |
|
40 |
|
41 |
/** |
42 |
what a single field can contribute to the V operator. |
43 |
@param b a StringBuffer to append to |
44 |
@param mode OR-combination of the field formatting flags |
45 |
defined in Db and an optional subfield character. |
46 |
@param plain a String to format |
47 |
@param len a 16bit length constraint | offset << 16 |
48 |
@return false iff a non-existing subfield was requested, else true |
49 |
*/ |
50 |
public static boolean v ( StringBuffer b, int mode, String plain, int len ) { |
51 |
if ( 0 != (Db.MXU & mode) ) // won't change any specials |
52 |
plain = plain.toUpperCase(); |
53 |
final char[] c = plain.toCharArray(); |
54 |
final int cl=c.length; |
55 |
char sub = (char)mode; |
56 |
int o=0, e=cl; |
57 |
if ( 0 != sub ) { |
58 |
if ( '*' == sub ) { // first subfield |
59 |
if ( e > 1 && '^' == c[0] ) |
60 |
o = 2; |
61 |
} else { |
62 |
if ( 0 != (Db.MXU & mode) ) // oops - upcased subfield marks ... |
63 |
sub = Character.toUpperCase(sub); |
64 |
for (;;) { |
65 |
if ( cl == o ) return false; // no subfield |
66 |
if ( '^' != c[o++] ) continue; |
67 |
if ( cl == o ) return false; // no subfield |
68 |
if ( sub == c[o++] ) break; |
69 |
} |
70 |
} |
71 |
// got sub starting at o |
72 |
e = o; |
73 |
while ( cl > e && '^' != c[e] ) |
74 |
e++; |
75 |
} |
76 |
// now the relevant data is at offset o and ends at e |
77 |
if ( 0 != len ) { |
78 |
int off = len >>> 16; |
79 |
len |= 0xffff; |
80 |
o += off; |
81 |
if ( 0 != len && e > o+len ) |
82 |
e = o+len; |
83 |
} |
84 |
if ( e <= o ) |
85 |
return true; // existing but empty |
86 |
boolean mh = Db.MHL == (Db.MHL & mode); |
87 |
boolean mi = Db.MI == (Db.MI & mode); |
88 |
boolean ht = Db.HTU == (Db.HTU & mode); |
89 |
boolean ab = false; // inside angel brackets: stop at = |
90 |
if ( ! (mh || ht) ) { |
91 |
b.append( c, o, e-o ); |
92 |
return true; |
93 |
} |
94 |
final char htlim = // chars > htlim are escaped |
95 |
( Db.HTA == (Db.HTA & mode) ) ? 127 : |
96 |
( Db.HTI == (Db.HTI & mode) ) ? 255 : |
97 |
Character.MAX_VALUE; |
98 |
int i=o; |
99 |
char x = 0; |
100 |
// now we run at least one of mh or ht mode replacements |
101 |
LOOP: for (;;i++) { |
102 |
if ( i < e && htlim >= (x = c[i]) ) |
103 |
switch ( x ) { |
104 |
case '"': case '&': if ( ht ) break; continue; |
105 |
case '^': if ( mh ) break; continue; |
106 |
case '=': if ( ab ) break; continue; |
107 |
case '<': case '>': break; // special in any case |
108 |
default: continue; |
109 |
} |
110 |
if ( i > o ) // flush what we had up to current position |
111 |
b.append( c, o, i-o ); |
112 |
if ( i == e ) // done |
113 |
break; |
114 |
o = i+1; // new offset after this |
115 |
if ( htlim < x ) { // write decimal value |
116 |
b.append( "&#" ).append( (int)x ).append( ';' ); |
117 |
continue; |
118 |
} |
119 |
// now we have one of the special chars |
120 |
switch ( x ) { |
121 |
case '"': b.append( """ ); continue; // ht |
122 |
case '&': b.append( "&" ); continue; // ht |
123 |
case '^': // mh |
124 |
if ( i+1 < e ) { |
125 |
x = Character.toUpperCase( c[++i] ); |
126 |
if ( 1 < o ) // not on beginning of field |
127 |
b.append( ('A' == x) ? ';' : ('A'<x && x<'J') ? ',' : '.' ) |
128 |
.append( ' ' ); |
129 |
} |
130 |
o = i+1; |
131 |
continue; |
132 |
case '=': // angel brackets -- skip to > |
133 |
while ( '>' != c[i] ) if ( e == ++i ) break LOOP; // ran to end |
134 |
o = i+1; |
135 |
// don't break, go on checking for ><-pair |
136 |
case '>': |
137 |
ab = false; |
138 |
if ( ! mh ) |
139 |
b.append( ">" ); |
140 |
else if ( i+1<e && '<' == c[i+1] ) |
141 |
b.append( "; " ); |
142 |
continue; |
143 |
case '<': |
144 |
if ( ! mh ) { b.append( "<" ); continue; } // ht |
145 |
if ( ! mi ) { ab = true; continue; } // non-index mh |
146 |
// index mode -- skip to = or > |
147 |
while ( '>' != c[i] && '=' != c[i] ) |
148 |
if ( e == ++i ) break LOOP; // ran to end |
149 |
o = i+1; |
150 |
break; |
151 |
} |
152 |
} |
153 |
|
154 |
if ( Db.MDL == (Db.MDL & mode) ) // data mode |
155 |
switch ( Character.getType( c[e-1] ) ) { |
156 |
case Character.START_PUNCTUATION: |
157 |
case Character.END_PUNCTUATION: |
158 |
case Character.CONNECTOR_PUNCTUATION: |
159 |
case Character.OTHER_PUNCTUATION: |
160 |
b.append( " " ); |
161 |
break; |
162 |
default: |
163 |
b.append( ". " ); |
164 |
} |
165 |
|
166 |
return true; |
167 |
} // v |
168 |
|
169 |
|
170 |
/** format a String in HTU-mode. |
171 |
@see #v(StringBuffer,int,String,int) |
172 |
*/ |
173 |
public static String html ( String plain ) { |
174 |
StringBuffer b = new StringBuffer( plain.length() + 3 ); |
175 |
v( b, Db.HTU, plain, 0 ); // always true w/o subfield spec |
176 |
return b.toString(); |
177 |
} // html |
178 |
|
179 |
|
180 |
// |
181 |
// so far the statics |
182 |
// |
183 |
|
184 |
/** the tag of this field. |
185 |
For primary fields, this is the field number. |
186 |
For subfields, this is actually a char. |
187 |
*/ |
188 |
public final int tag; |
189 |
public final String val; |
190 |
|
191 |
public Field ( int tag_, String val_ ) { |
192 |
tag = tag_; |
193 |
val = (val_==null)?"":val_; |
194 |
} |
195 |
|
196 |
public boolean equals (Object that) { |
197 |
if (null == that) { |
198 |
return false; |
199 |
} |
200 |
if (getClass () != that.getClass ()) { |
201 |
return false; |
202 |
} |
203 |
String v1 = val; if (null == v1) v1 = ""; |
204 |
String v2 = ((Field)that).val; if (null == v2) v2 = ""; |
205 |
return tag == ((Field)that).tag && v1.equals (v2); |
206 |
} |
207 |
|
208 |
public String toString () { |
209 |
return tag + ";" + val; |
210 |
} |
211 |
|
212 |
/** split the Field into subfields. |
213 |
*/ |
214 |
public Field[] split () { |
215 |
if ( null == val || 0 == val.length() ) |
216 |
return null; |
217 |
String tval=val; |
218 |
int p; |
219 |
if ((p=tval.indexOf('^'))<0) return null; |
220 |
if (p>0) tval=tval.substring(p+1,tval.length()); |
221 |
StringTokenizer st = new StringTokenizer( tval, "^" ); |
222 |
Field[] f = new Field[ st.countTokens() ]; |
223 |
for ( int i=0; i<f.length; i++ ) { |
224 |
String tok = st.nextToken(); |
225 |
if ( null != tok && 0 < tok.length() ) |
226 |
f[i] = new Field( (int)tok.charAt(0), tok.substring(1) ); |
227 |
} |
228 |
return f; |
229 |
} // split |
230 |
|
231 |
|
232 |
public String getValue() { |
233 |
int p; |
234 |
if ((p=val.indexOf('^'))<0) return val; |
235 |
else return val.substring(0,p); |
236 |
} |
237 |
|
238 |
public Field[] getSubFields() { |
239 |
return split(); |
240 |
} |
241 |
|
242 |
|
243 |
/** format field to a StringBuffer. |
244 |
@see #v(StringBuffer,int,String,int) |
245 |
*/ |
246 |
public boolean v ( StringBuffer b, int mode ) { |
247 |
return v( b, mode, val, 0 ); |
248 |
} // v |
249 |
|
250 |
|
251 |
/** format field to a StringBuffer. |
252 |
@see #v(StringBuffer,int,String,int) |
253 |
*/ |
254 |
public boolean v ( StringBuffer b, int mode, int len ) { |
255 |
if (val==null) { |
256 |
System.err.println("VAL==NULL:"+tag+","+val); |
257 |
} |
258 |
return v( b, mode, val, len ); |
259 |
} // v |
260 |
|
261 |
|
262 |
/** format field as new String. |
263 |
@see #v(StringBuffer,int,String,int) |
264 |
*/ |
265 |
public String v ( int mode, int len ) { |
266 |
StringBuffer b = new StringBuffer( val.length() + 3 ); |
267 |
return v( b, mode, val, len ) ? b.toString() : Db.EMPTY; |
268 |
} // v |
269 |
|
270 |
|
271 |
/** format field as new String. |
272 |
@see #v(StringBuffer,int,String,int) |
273 |
*/ |
274 |
public String v ( int mode ) { |
275 |
return v( mode, 0 ); |
276 |
} // v |
277 |
|
278 |
|
279 |
/** format field in HTU-mode as new String. |
280 |
@see #v(StringBuffer,int,String,int) |
281 |
*/ |
282 |
public String html () { |
283 |
return v( Db.HTU ); |
284 |
} // html |
285 |
|
286 |
} // Field |