~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to subversion/main/src/org/netbeans/modules/subversion/client/parser/WorkingCopyDetails.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
package org.netbeans.modules.subversion.client.parser;
 
42
 
 
43
import java.io.BufferedInputStream;
 
44
import java.io.BufferedReader;
 
45
import java.io.File;
 
46
import java.io.IOException;
 
47
import java.io.InputStream;
 
48
import java.text.ParseException;
 
49
import java.util.ArrayList;
 
50
import java.util.Arrays;
 
51
import java.util.Date;
 
52
import java.util.List;
 
53
import java.util.Map;
 
54
import org.netbeans.modules.subversion.config.KVFile;
 
55
import org.openide.ErrorManager;
 
56
 
 
57
/**
 
58
 *
 
59
 * @author Ed Hillmann
 
60
 */
 
61
public class WorkingCopyDetails {
 
62
    static final String FILE_ATTRIBUTE_VALUE = "file";  // NOI18
 
63
    static final String IS_HANDLED = "handled";
 
64
    private static final char SLASH_N = '\n';
 
65
    private static final char SLASH_R = '\r';
 
66
 
 
67
    static final String VERSION_ATTR_KEY = "wc-version";
 
68
    static final String VERSION_UNKNOWN = "";
 
69
    static final String VERSION_13 = "1.3";
 
70
    static final String VERSION_14 = "1.4";
 
71
 
 
72
    private final File file;
 
73
    //These Map stores the values in the SVN entities file
 
74
    //for the file and its parent directory
 
75
    private final Map<String, String> attributes;
 
76
    //private Properties parentProps;
 
77
    //These Properties store the working and base versions of the
 
78
    //SVN properties for the file
 
79
    private Map<String, byte[]> workingSvnProperties = null;
 
80
    private Map<String, byte[]> baseSvnProperties = null;
 
81
 
 
82
    protected File propertiesFile = null;
 
83
    private File basePropertiesFile = null;
 
84
    private File textBaseFile = null;
 
85
 
 
86
    /** Creates a new instance of WorkingCopyDetails */
 
87
    private WorkingCopyDetails(File file, Map<String, String> attributes) {
 
88
        this.file = file;
 
89
        this.attributes  = attributes;
 
90
    }
 
91
 
 
92
    public static WorkingCopyDetails createWorkingCopy(File file, Map<String, String> attributes) {
 
93
        String version = attributes != null ? attributes.get(VERSION_ATTR_KEY) : VERSION_UNKNOWN;
 
94
        if(version != null) {
 
95
            if(version.equals(VERSION_13)) {
 
96
 
 
97
                return new WorkingCopyDetails(file, attributes);
 
98
                
 
99
            } else if(version.equals(VERSION_14)) {
 
100
                
 
101
                return new WorkingCopyDetails(file, attributes) {
 
102
                    public boolean propertiesExist() throws IOException {
 
103
                        return getAttributes().containsKey("has-props");        // NOI18N
 
104
                    }  
 
105
                    public boolean propertiesModified() throws IOException {
 
106
                        return getAttributes().containsKey("has-prop-mods");    // NOI18N
 
107
                    }            
 
108
                    File getPropertiesFile() throws IOException {
 
109
                        if (propertiesFile == null) {
 
110
                            // unchanged properties have only the base file
 
111
                            boolean modified = getBooleanValue("has-prop-mods");                                // NOI18N
 
112
                            propertiesFile = SvnWcUtils.getPropertiesFile(getFile(), modified ? false : true);
 
113
                        }
 
114
                        return propertiesFile;
 
115
                    }                    
 
116
                };
 
117
 
 
118
            } else if(version.equals(VERSION_UNKNOWN)) {
 
119
 
 
120
                WorkingCopyDetails wcd = new WorkingCopyDetails(file, attributes);
 
121
                if(!wcd.isHandled()) {
 
122
                    return wcd;
 
123
                } 
 
124
                // how is this possible?
 
125
                throw new UnsupportedOperationException("Unknown SVN working copy version: " + version);    // NOI18N                
 
126
 
 
127
            } else {
 
128
 
 
129
                throw new UnsupportedOperationException("Unknown SVN working copy version: " + version);    // NOI18N
 
130
 
 
131
            }   
 
132
        } else {
 
133
            ErrorManager.getDefault().log(ErrorManager.WARNING, "Could not determine the SVN working copy version for " + file + ". Falling back on 1.3");  // NOI18N
 
134
            return new WorkingCopyDetails(file, attributes);
 
135
        }
 
136
    }
 
137
 
 
138
    protected Map<String, String> getAttributes() { 
 
139
        return attributes;
 
140
    }
 
141
    
 
142
    protected File getFile() {
 
143
        return file;
 
144
    }
 
145
    
 
146
    public String getValue(String propertyName, String defaultValue) {
 
147
        String returnValue = getValue(propertyName);
 
148
        return returnValue != null ? returnValue : defaultValue;
 
149
    }
 
150
 
 
151
    public String getValue(String key) {
 
152
        if(key==null) return null;
 
153
        return getAttributes() != null ? getAttributes().get(key) : null;
 
154
    }
 
155
 
 
156
    public long getLongValue(String key) throws LocalSubversionException {
 
157
        try {
 
158
            String value = getValue(key);
 
159
            if(value==null) return -1;
 
160
            return Long.parseLong(value);
 
161
        } catch (NumberFormatException ex) {
 
162
            throw new LocalSubversionException(ex);
 
163
        }
 
164
    }
 
165
 
 
166
    public Date getDateValue(String key) throws LocalSubversionException {
 
167
        try {
 
168
            String value = getValue(key);
 
169
            if(value==null || value.equals("")) return null;
 
170
            return SvnWcUtils.parseSvnDate(value);
 
171
        } catch (ParseException ex) {
 
172
            throw new LocalSubversionException(ex);
 
173
        }
 
174
    }
 
175
 
 
176
    public boolean getBooleanValue(String key) {
 
177
        String value = getValue(key);
 
178
        if(value==null) return false;
 
179
        return Boolean.valueOf(value).booleanValue();
 
180
    }
 
181
 
 
182
    public boolean isHandled() {
 
183
        return getBooleanValue(IS_HANDLED);
 
184
    }
 
185
 
 
186
    public boolean isFile() {
 
187
        return getAttributes() !=null ? FILE_ATTRIBUTE_VALUE.equals(getAttributes().get("kind")) : false; // NOI18N
 
188
    }
 
189
 
 
190
    File getPropertiesFile() throws IOException {
 
191
        if (propertiesFile == null) {
 
192
            propertiesFile = SvnWcUtils.getPropertiesFile(file, false);
 
193
        }
 
194
        return propertiesFile;
 
195
    }
 
196
 
 
197
    File getBasePropertiesFile() throws IOException {
 
198
        if (basePropertiesFile == null) {
 
199
            basePropertiesFile = SvnWcUtils.getPropertiesFile(file, true);
 
200
        }
 
201
        return basePropertiesFile;
 
202
    }
 
203
 
 
204
    private File getTextBaseFile() throws IOException {
 
205
        if (textBaseFile == null) {
 
206
            textBaseFile = SvnWcUtils.getTextBaseFile(file);
 
207
        }
 
208
        return textBaseFile;
 
209
    }
 
210
 
 
211
    private Map<String, byte[]> getWorkingSvnProperties() throws IOException {
 
212
        if (workingSvnProperties == null) {
 
213
            workingSvnProperties = loadProperties(getPropertiesFile());
 
214
        }
 
215
        return workingSvnProperties;
 
216
    }
 
217
 
 
218
    private Map<String, byte[]> getBaseSvnProperties() throws IOException {
 
219
        if (baseSvnProperties == null) {
 
220
            baseSvnProperties = loadProperties(getBasePropertiesFile());
 
221
        }
 
222
        return baseSvnProperties;
 
223
    }
 
224
 
 
225
    public boolean propertiesExist() throws IOException {
 
226
        boolean returnValue = false;
 
227
        File propsFile = getPropertiesFile();
 
228
        returnValue = propsFile != null ? propsFile.exists() : false;
 
229
        if (returnValue) {
 
230
            //A size of 4 bytes is equivalent to empty properties
 
231
            InputStream inputStream = new java.io.FileInputStream(propsFile);
 
232
            try {
 
233
                int size = 0;
 
234
                int retval = inputStream.read();
 
235
                while ((retval != -1) && (size < 5)) {
 
236
                    size++;
 
237
                    retval = inputStream.read();
 
238
                }
 
239
                returnValue = (size > 4);
 
240
            } finally {
 
241
                inputStream.close();
 
242
            }
 
243
        }
 
244
 
 
245
        return returnValue;
 
246
    }
 
247
 
 
248
    public boolean propertiesModified() throws IOException {        
 
249
        Map<String, byte[]> baseProps = getBaseSvnProperties();
 
250
        Map<String, byte[]> props = getWorkingSvnProperties();
 
251
        if ((baseProps == null) && (props != null)) {
 
252
            return true;
 
253
        }
 
254
        if ((baseProps != null) && (props == null)) {
 
255
            return true;
 
256
        }
 
257
        if ((baseProps == null) && (props == null)) {
 
258
            return false;
 
259
        }        
 
260
        if(baseProps.size() != props.size()) {
 
261
            return true;
 
262
        }
 
263
        for(Map.Entry<String, byte[]> baseEntry : baseProps.entrySet()) {
 
264
            byte[] propsValue = props.get(baseEntry.getKey());            
 
265
            if(propsValue == null || !Arrays.equals(propsValue, baseEntry.getValue())) {
 
266
                return true;
 
267
            }                        
 
268
        }
 
269
        return false;
 
270
    }
 
271
 
 
272
    private Map<String, byte[]> loadProperties(File propsFile) throws IOException {
 
273
        if(propsFile == null || !propsFile.exists()) {
 
274
            return null;
 
275
        }        
 
276
        KVFile kv = new KVFile(propsFile);
 
277
        return kv.getNormalizedMap();                
 
278
    }
 
279
 
 
280
    public boolean textModified() throws IOException {
 
281
        if (file.exists()) {
 
282
            File baseFile = getTextBaseFile();
 
283
            if ((file == null) && (baseFile != null)) {
 
284
                return true;
 
285
            }
 
286
            if ((file != null) && (baseFile == null)) {
 
287
                return true;
 
288
            }
 
289
            if ((file == null) && (baseFile == null)) {
 
290
                return false;
 
291
            }
 
292
            Map<String, byte[]> workingSvnProps = getWorkingSvnProperties();
 
293
            if(workingSvnProps != null) {                
 
294
                String svnSpecial = getPropertyValue(workingSvnProps, "svn:special");           // NOI18N
 
295
                if (svnSpecial != null && svnSpecial.equals("*")) {                             // NOI18N
 
296
                    if (isSymbolicLink()) {
 
297
                        return false;
 
298
                    }
 
299
                }                   
 
300
                String svnKeywords = getPropertyValue(workingSvnProps, "svn:keywords");         // NOI18N          
 
301
                if (svnKeywords != null) {
 
302
                    return isModifiedByLine(svnKeywords.trim());
 
303
                } 
 
304
            }
 
305
            return isModifiedByByte();            
 
306
        }
 
307
        return false;
 
308
    }
 
309
 
 
310
    private String getPropertyValue(Map<String, byte[]> props, String key) {
 
311
        byte[] byteValue = props.get(key);
 
312
        if(byteValue != null && byteValue.length > 0) {
 
313
            return new String(byteValue);
 
314
        }        
 
315
        return  null;
 
316
    }
 
317
    
 
318
    private boolean isSymbolicLink() throws IOException {
 
319
        boolean returnValue = false;
 
320
 
 
321
        File baseFile = getTextBaseFile();
 
322
        if (baseFile != null) {
 
323
            BufferedReader reader = null;
 
324
            try {
 
325
                reader = new BufferedReader(new java.io.FileReader(baseFile));
 
326
                String firstLine = reader.readLine();
 
327
                returnValue = firstLine.startsWith("link");     // NOI18N
 
328
            } finally {
 
329
                if (reader != null) {
 
330
                    reader.close();
 
331
                }
 
332
            }
 
333
        }   
 
334
        return returnValue;
 
335
    }
 
336
 
 
337
    /**
 
338
     * Assumes that textBaseFile exists
 
339
     */
 
340
    private boolean isModifiedByByte() throws IOException {
 
341
        boolean returnValue = false;
 
342
 
 
343
        InputStream baseStream = null;
 
344
        InputStream fileStream = null;
 
345
        try {
 
346
            baseStream = new BufferedInputStream(new java.io.FileInputStream(textBaseFile));
 
347
            fileStream = new BufferedInputStream(new java.io.FileInputStream(file));
 
348
 
 
349
            int baseRetVal = baseStream.read();
 
350
            int fileRetVal =  fileStream.read();
 
351
 
 
352
            while (baseRetVal != -1) {
 
353
                if (baseRetVal != fileRetVal) {
 
354
                    //Check for line endings... ignore if need be
 
355
                    boolean isLineEnding = false;
 
356
                    if ((SLASH_N == ((char) baseRetVal)) && SLASH_R == ((char) fileRetVal)) {
 
357
                        fileRetVal = fileStream.read();
 
358
                        isLineEnding = (SLASH_N == ((char) fileRetVal));
 
359
                    } else if ((SLASH_N == ((char) fileRetVal)) && SLASH_R == ((char) baseRetVal)) {
 
360
                        baseRetVal = baseStream.read();
 
361
                        isLineEnding = (SLASH_N == ((char) baseRetVal));
 
362
                    }
 
363
 
 
364
                    if (!(isLineEnding)) {
 
365
                        return true;
 
366
                    }
 
367
                }
 
368
                baseRetVal = baseStream.read();
 
369
                fileRetVal = fileStream.read();
 
370
            }
 
371
 
 
372
            //If we're here, then baseRetVal is -1.  So, returnValue
 
373
            //should be true is fileRetVal != -1
 
374
            returnValue = (fileRetVal != -1);
 
375
        } finally {
 
376
            if(fileStream != null) {
 
377
                fileStream.close();
 
378
            }
 
379
            if(baseStream != null) {
 
380
                baseStream.close();
 
381
            }
 
382
        }
 
383
 
 
384
        return returnValue;
 
385
    }
 
386
 
 
387
    /**
 
388
     * Assumes that textBaseFile exists
 
389
     */
 
390
    private boolean isModifiedByLine(String rawKeywords) throws IOException {
 
391
        if(rawKeywords == null || rawKeywords.equals("")) {
 
392
            return false;
 
393
        }
 
394
        boolean returnValue = false;
 
395
 
 
396
        List<String> keywordsList = new ArrayList<String>();
 
397
        
 
398
        rawKeywords = rawKeywords.replaceAll("\n", " ");
 
399
        rawKeywords = rawKeywords.replaceAll("\t", " ");        
 
400
        keywordsList.addAll(normalizeKeywords(rawKeywords.split(" ")));             // NOI18N          
 
401
 
 
402
        String[] keywords = keywordsList.toArray(new String[keywordsList.size()]);
 
403
        
 
404
        BufferedReader baseReader = null;
 
405
        BufferedReader fileReader = null;
 
406
 
 
407
        try {
 
408
            baseReader = new BufferedReader(new java.io.FileReader(textBaseFile));
 
409
            fileReader = new BufferedReader(new java.io.FileReader(file));
 
410
 
 
411
            String baseLine = baseReader.readLine();
 
412
            String fileLine = fileReader.readLine();
 
413
 
 
414
            while (baseLine != null && fileLine != null) {                
 
415
                            
 
416
                if (!fileLine.equals(baseLine)) {
 
417
                    boolean equal = false;
 
418
                    for (int i = 0; i < keywords.length; i++) {
 
419
                        String headerPattern = "$" + keywords[i];                           // NOI18N
 
420
                        if(fileLine.indexOf(headerPattern) > -1) {
 
421
                            equal = compareKeywordLines(fileLine, baseLine, keywords);
 
422
                            break;
 
423
                        }                    
 
424
                    }
 
425
                    if(!equal) {
 
426
                        return true;
 
427
                    }
 
428
                }
 
429
 
 
430
                baseLine = baseReader.readLine();
 
431
                fileLine = fileReader.readLine();
 
432
            }
 
433
 
 
434
            returnValue = baseLine != null || fileLine != null;
 
435
            
 
436
        } finally {
 
437
            if (fileReader != null) {
 
438
                fileReader.close();
 
439
            }
 
440
 
 
441
            if (baseReader != null) {
 
442
                baseReader.close();
 
443
            }
 
444
        }
 
445
 
 
446
        return returnValue;
 
447
    }
 
448
 
 
449
    private List<String> normalizeKeywords(String[] keywords) {
 
450
        List<String> keywordsList = new ArrayList<String>();
 
451
        for (int i = 0; i < keywords.length; i++) {
 
452
            String kw = keywords[i].toLowerCase().trim();
 
453
            if(kw.equals("date") || kw.equals("lastchangeddate")) {                                         // NOI18N
 
454
                keywordsList.add("LastChangedDate");                                                        // NOI18N
 
455
                keywordsList.add("Date");                                                                   // NOI18N
 
456
            } else if(kw.equals("revision") || kw.equals("rev") || kw.equals("lastchangedrevision")) {      // NOI18N
 
457
                keywordsList.add("LastChangedRevision");                                                    // NOI18N
 
458
                keywordsList.add("Revision");                                                               // NOI18N
 
459
                keywordsList.add("Rev");                                                                    // NOI18N
 
460
            } else if(kw.equals("author") || kw.equals("lastchangedby")) {                                  // NOI18N
 
461
                keywordsList.add("LastChangedBy");                                                          // NOI18N
 
462
                keywordsList.add("Author");                                                                 // NOI18N
 
463
            } else if(kw.equals("url") || kw.equals("headurl")) {                                           // NOI18N
 
464
                keywordsList.add("HeadURL");                                                                // NOI18N
 
465
                keywordsList.add("URL");                                                                    // NOI18N
 
466
            } else if(kw.equals("id")) {                                                                    // NOI18N
 
467
                keywordsList.add("Id");                                                                     // NOI18N                
 
468
            } else if(!kw.equals("")){
 
469
                keywordsList.add(keywords[i]);                
 
470
            }            
 
471
        } 
 
472
        return keywordsList;
 
473
    }
 
474
    
 
475
    private boolean compareKeywordLines(String modifiedLine, String baseLine, String[] keywords) {
 
476
 
 
477
        int modifiedIdx = 0;
 
478
        for (int fileIdx = 0; fileIdx < baseLine.length(); fileIdx++) {
 
479
 
 
480
            if(baseLine.charAt(fileIdx) == '$') {
 
481
                // 1. could be a keyword ...
 
482
                for (int keywordsIdx = 0; keywordsIdx < keywords.length; keywordsIdx++) {
 
483
 
 
484
                    String keyword = keywords[keywordsIdx];                                          // NOI18N
 
485
 
 
486
                    boolean gotHeader = false;
 
487
                    for (int keyIdx = 0; keyIdx < keyword.length(); keyIdx++) {
 
488
                        if(fileIdx + keyIdx + 1 > baseLine.length() - 1 ||                           // we are already at the end of the baseline
 
489
                           keyword.charAt(keyIdx) != baseLine.charAt(fileIdx + keyIdx + 1))          // the chars are not equal
 
490
                        {    
 
491
                            gotHeader = false;
 
492
                            break; // 2. it's not a keyword -> try the next one
 
493
                        } 
 
494
                        gotHeader = true;
 
495
                    }
 
496
                    
 
497
                    if(gotHeader) {
 
498
                        
 
499
                        // base file idx
 
500
                        fileIdx += keyword.length(); 
 
501
                        
 
502
                        // 3. now check if there is somthing like "$", ":$" after the keyword                                
 
503
                        if(checkFollowingString(baseLine, fileIdx + 1, "$")) {
 
504
                            fileIdx += 1;
 
505
                        } else if(checkFollowingString(baseLine, fileIdx + 1, ":$")) { 
 
506
                            fileIdx += 2;    
 
507
                        } else if(checkFollowingString(baseLine, fileIdx + 1, "::")) {
 
508
                            int spaces = getSpacesCount(baseLine, fileIdx + 3);
 
509
                            if(spaces <= 0) {
 
510
                                return false;
 
511
                            }
 
512
                            fileIdx += spaces + 3;                                
 
513
                        } else {
 
514
                            // it's not a keyword -> rollback the index and keep comparing 
 
515
                            fileIdx -= keyword.length();
 
516
                            break; 
 
517
                        }
 
518
                                                
 
519
                        // 4. it was a correctly closed keyword -> skip the chars until the next '$'
 
520
                        // for the modified file - '$Id: '
 
521
                        modifiedIdx += keyword.length() + 1;       //                  
 
522
                        while(++modifiedIdx < modifiedLine.length() && modifiedLine.charAt(modifiedIdx) != '$');
 
523
 
 
524
                        if(modifiedIdx >= modifiedLine.length()) {
 
525
                            // modified line is done but we found a keyword -> wrong
 
526
                            return false; 
 
527
                        } 
 
528
                        break;
 
529
                    }
 
530
                }
 
531
            }            
 
532
            if(modifiedLine.charAt(modifiedIdx) != baseLine.charAt(fileIdx)) {
 
533
                return false; 
 
534
            }
 
535
            modifiedIdx++;
 
536
            if(modifiedIdx >= modifiedLine.length()) {
 
537
                // if the modified line is done then must be also the base line done
 
538
                return fileIdx == baseLine.length() - 1;
 
539
            }
 
540
        }
 
541
        return modifiedIdx == modifiedLine.length() - 2;      
 
542
    }
 
543
    
 
544
    private boolean checkFollowingString(String baseLine, int offset, String str) {
 
545
        if(baseLine.length() < offset + str.length()) {
 
546
            return false;
 
547
        }
 
548
        for (int idx = 0; idx < str.length(); idx++) {
 
549
            if(baseLine.charAt(offset + idx) != str.charAt(idx)) {
 
550
                return false;
 
551
            }            
 
552
        }
 
553
        return true;    
 
554
    }
 
555
    
 
556
    private int getSpacesCount(String baseLine, int offset) {
 
557
        if(baseLine.length() <= offset) {
 
558
            return -1;
 
559
        }
 
560
        for (int idx = 0; idx < baseLine.length(); idx++) {
 
561
            char c = baseLine.charAt(offset + idx);
 
562
            if(c == ' ') {
 
563
                continue;
 
564
            } else if(c == '$') {
 
565
                return idx;
 
566
            }            
 
567
            return -1;
 
568
        }
 
569
        return -1;
 
570
    }
 
571
    
 
572
}