source: osm/applications/editors/josm/plugins/opendata/src/org/geotools/data/shapefile/files/TabFiles.java@ 33518

Last change on this file since 33518 was 33518, checked in by donvip, 7 years ago

update to JOSM 12649

File size: 33.7 KB
Line 
1// JOSM opendata plugin.
2// Copyright (C) 2011-2012 Don-vip
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16package org.geotools.data.shapefile.files;
17
18import static org.geotools.data.shapefile.files.ShpFileType.SHP;
19
20import java.io.File;
21import java.io.FileOutputStream;
22import java.io.FilenameFilter;
23import java.io.FilterInputStream;
24import java.io.FilterOutputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.io.RandomAccessFile;
29import java.net.MalformedURLException;
30import java.net.URL;
31import java.net.URLConnection;
32import java.nio.MappedByteBuffer;
33import java.nio.channels.Channels;
34import java.nio.channels.FileChannel;
35import java.nio.channels.FileChannel.MapMode;
36import java.nio.channels.ReadableByteChannel;
37import java.nio.channels.WritableByteChannel;
38import java.util.ArrayList;
39import java.util.Collection;
40import java.util.HashMap;
41import java.util.Map;
42import java.util.Map.Entry;
43import java.util.Set;
44import java.util.concurrent.ConcurrentHashMap;
45import java.util.concurrent.locks.ReentrantReadWriteLock;
46import java.util.logging.Level;
47
48import org.geotools.data.DataUtilities;
49import org.openstreetmap.josm.tools.Logging;
50
51/**
52 * Ugly copy of ShpFiles class modified to fit MapInfo TAB needs.
53 */
54public class TabFiles extends ShpFiles {
55
56 /**
57 * The urls for each type of file that is associated with the shapefile. The
58 * key is the type of file
59 */
60 private final Map<ShpFileType, URL> urls = new ConcurrentHashMap<>();
61
62 /**
63 * A read/write lock, so that we can have concurrent readers
64 */
65 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
66
67 /**
68 * The set of locker sources per thread. Used as a debugging aid and to upgrade/downgrade
69 * the locks
70 */
71 private final Map<Thread, Collection<ShpFilesLocker>> lockers = new ConcurrentHashMap<>();
72
73 /**
74 * A cache for read only memory mapped buffers
75 */
76 private final MemoryMapCache mapCache = new MemoryMapCache();
77
78 private boolean memoryMapCacheEnabled;
79
80
81 ////////////////////////////////////////////////////
82
83 public TabFiles(File headerFile, File dataFile) throws IllegalArgumentException {
84 super(fakeShpFile(headerFile)); // Useless but necessary
85 init(DataUtilities.fileToURL(headerFile));
86 urls.put(ShpFileType.DBF, DataUtilities.fileToURL(dataFile));
87 }
88
89 /**
90 * This is really ugly. Used only to give a fake shp file to ShpFiles constructor to avoid IllegalArgument at initialization.
91 */
92 private static URL fakeShpFile(File headerFile) {
93 return DataUtilities.fileToURL(new File(headerFile.getAbsolutePath()+".shp"));
94 }
95
96 private String baseName(Object obj) {
97 if (obj instanceof URL) {
98 return toBase(((URL) obj).toExternalForm());
99 }
100 return null;
101 }
102
103 private String toBase(String path) {
104 return path.substring(0, path.toLowerCase().lastIndexOf(".tab"));
105 }
106
107 ////////////////////////////////////////////////////
108
109 private void init(URL url) {
110 String base = baseName(url);
111 if (base == null) {
112 throw new IllegalArgumentException(
113 url.getPath()
114 + " is not one of the files types that is known to be associated with a MapInfo TAB file");
115 }
116
117 String urlString = url.toExternalForm();
118 char lastChar = urlString.charAt(urlString.length()-1);
119 boolean upperCase = Character.isUpperCase(lastChar);
120
121 for (ShpFileType type : ShpFileType.values()) {
122
123 String extensionWithPeriod = type.extensionWithPeriod;
124 if (upperCase) {
125 extensionWithPeriod = extensionWithPeriod.toUpperCase();
126 } else {
127 extensionWithPeriod = extensionWithPeriod.toLowerCase();
128 }
129
130 URL newURL;
131 String string = base + extensionWithPeriod;
132 try {
133 newURL = new URL(url, string);
134 } catch (MalformedURLException e) {
135 // shouldn't happen because the starting url was constructable
136 throw new RuntimeException(e);
137 }
138 urls.put(type, newURL);
139 }
140
141 // if the files are local check each file to see if it exists
142 // if not then search for a file of the same name but try all combinations of the
143 // different cases that the extension can be made up of.
144 // IE Shp, SHP, Shp, ShP etc...
145 if (isLocalTab()) {
146 Set<Entry<ShpFileType, URL>> entries = urls.entrySet();
147 Map<ShpFileType, URL> toUpdate = new HashMap<>();
148 for (Entry<ShpFileType, URL> entry : entries) {
149 if( !existsTab(entry.getKey()) ){
150 url = findExistingFile(entry.getKey(), entry.getValue());
151 if( url!=null ){
152 toUpdate.put(entry.getKey(), url);
153 }
154 }
155 }
156 urls.putAll(toUpdate);
157 }
158 }
159
160 private URL findExistingFile(ShpFileType shpFileType, URL value) {
161 final File file = DataUtilities.urlToFile(value);
162 File directory = file.getParentFile();
163 if( directory==null || !directory.exists() ) {
164 // doesn't exist
165 return null;
166 }
167 File[] files = directory.listFiles(new FilenameFilter(){
168
169 @Override
170 public boolean accept(File dir, String name) {
171 return file.getName().equalsIgnoreCase(name);
172 }
173
174 });
175 if( files.length>0 ){
176 try {
177 return files[0].toURI().toURL();
178 } catch (MalformedURLException e) {
179 Logging.error(e);
180 }
181 }
182 return null;
183 }
184
185 /**
186 * This verifies that this class has been closed correctly (nothing locking)
187 */
188 @Override
189 protected void finalize() throws Throwable {
190 super.finalize();
191 dispose();
192 }
193
194 @Override
195 public void dispose() {
196 if (numberOfLocks() != 0) {
197 logCurrentLockers(Level.SEVERE);
198 lockers.clear(); // so as not to get this log again.
199 }
200 mapCache.clean();
201 }
202
203 /**
204 * Writes to the log all the lockers and when they were constructed.
205 *
206 * @param logLevel
207 * the level at which to log.
208 */
209 @Override
210 public void logCurrentLockers(Level logLevel) {
211 for (Collection<ShpFilesLocker> lockerList : lockers.values()) {
212 for (ShpFilesLocker locker : lockerList) {
213 StringBuilder sb = new StringBuilder("The following locker still has a lock: ");
214 sb.append(locker);
215 Logging.error(sb.toString());
216 }
217 }
218 }
219
220 /**
221 * Returns the URLs (in string form) of all the files for the shapefile
222 * datastore.
223 *
224 * @return the URLs (in string form) of all the files for the shapefile
225 * datastore.
226 */
227 @Override
228 public Map<ShpFileType, String> getFileNames() {
229 Map<ShpFileType, String> result = new HashMap<>();
230 Set<Entry<ShpFileType, URL>> entries = urls.entrySet();
231
232 for (Entry<ShpFileType, URL> entry : entries) {
233 result.put(entry.getKey(), entry.getValue().toExternalForm());
234 }
235
236 return result;
237 }
238
239 /**
240 * Returns the string form of the url that identifies the file indicated by
241 * the type parameter or null if it is known that the file does not exist.
242 *
243 * <p>
244 * Note: a URL should NOT be constructed from the string instead the URL
245 * should be obtained through calling one of the aquireLock methods.
246 *
247 * @param type
248 * indicates the type of file the caller is interested in.
249 *
250 * @return the string form of the url that identifies the file indicated by
251 * the type parameter or null if it is known that the file does not
252 * exist.
253 */
254 @Override
255 public String get(ShpFileType type) {
256 return urls.get(type).toExternalForm();
257 }
258
259 /**
260 * Returns the number of locks on the current set of shapefile files. This
261 * is not thread safe so do not count on it to have a completely accurate
262 * picture but it can be useful debugging
263 *
264 * @return the number of locks on the current set of shapefile files.
265 */
266 @Override
267 public int numberOfLocks() {
268 int count = 0;
269 for (Collection<ShpFilesLocker> lockerList : lockers.values()) {
270 count += lockerList.size();
271 }
272 return count;
273 }
274 /**
275 * Acquire a File for read only purposes. It is recommended that get*Stream or
276 * get*Channel methods are used when reading or writing to the file is
277 * desired.
278 *
279 *
280 * @see #getInputStream(ShpFileType, FileReader)
281 * @see #getReadChannel(ShpFileType, FileReader)
282 * @see #getWriteChannel(ShpFileType, FileReader)
283 *
284 * @param type
285 * the type of the file desired.
286 * @param requestor
287 * the object that is requesting the File. The same object
288 * must release the lock and is also used for debugging.
289 * @return the File type requested
290 */
291 @Override
292 public File acquireReadFile(ShpFileType type,
293 FileReader requestor) {
294 if(!isLocalTab() ){
295 throw new IllegalStateException("This method only applies if the files are local");
296 }
297 URL url = acquireRead(type, requestor);
298 return DataUtilities.urlToFile(url);
299 }
300
301 /**
302 * Acquire a URL for read only purposes. It is recommended that get*Stream or
303 * get*Channel methods are used when reading or writing to the file is
304 * desired.
305 *
306 *
307 * @see #getInputStream(ShpFileType, FileReader)
308 * @see #getReadChannel(ShpFileType, FileReader)
309 * @see #getWriteChannel(ShpFileType, FileReader)
310 *
311 * @param type
312 * the type of the file desired.
313 * @param requestor
314 * the object that is requesting the URL. The same object
315 * must release the lock and is also used for debugging.
316 * @return the URL to the file of the type requested
317 */
318 @Override
319 public URL acquireRead(ShpFileType type, FileReader requestor) {
320 URL url = urls.get(type);
321 if (url == null)
322 return null;
323
324 readWriteLock.readLock().lock();
325 Collection<ShpFilesLocker> threadLockers = getCurrentThreadLockers();
326 threadLockers.add(new ShpFilesLocker(url, requestor));
327 return url;
328 }
329
330 /**
331 * Tries to acquire a URL for read only purposes. Returns null if the
332 * acquire failed or if the file does not.
333 * <p> It is recommended that get*Stream or
334 * get*Channel methods are used when reading or writing to the file is
335 * desired.
336 * </p>
337 *
338 * @see #getInputStream(ShpFileType, FileReader)
339 * @see #getReadChannel(ShpFileType, FileReader)
340 * @see #getWriteChannel(ShpFileType, FileReader)
341 *
342 * @param type
343 * the type of the file desired.
344 * @param requestor
345 * the object that is requesting the URL. The same object
346 * must release the lock and is also used for debugging.
347 * @return A result object containing the URL or the reason for the failure.
348 */
349 @Override
350 public Result<URL, State> tryAcquireRead(ShpFileType type,
351 FileReader requestor) {
352 URL url = urls.get(type);
353 if (url == null) {
354 return new Result<>(null, State.NOT_EXIST);
355 }
356
357 boolean locked = readWriteLock.readLock().tryLock();
358 if (!locked) {
359 return new Result<>(null, State.LOCKED);
360 }
361
362 getCurrentThreadLockers().add(new ShpFilesLocker(url, requestor));
363
364 return new Result<>(url, State.GOOD);
365 }
366
367 /**
368 * Unlocks a read lock. The file and requestor must be the the same as the
369 * one of the lockers.
370 *
371 * @param file
372 * file that was locked
373 * @param requestor
374 * the class that requested the file
375 */
376 @Override
377 public void unlockRead(File file, FileReader requestor) {
378 Collection<URL> allURLS = urls.values();
379 for (URL url : allURLS) {
380 if (DataUtilities.urlToFile(url).equals(file)) {
381 unlockRead(url, requestor);
382 }
383 }
384 }
385
386 /**
387 * Unlocks a read lock. The url and requestor must be the the same as the
388 * one of the lockers.
389 *
390 * @param url
391 * url that was locked
392 * @param requestor
393 * the class that requested the url
394 */
395 @Override
396 public void unlockRead(URL url, FileReader requestor) {
397 if (url == null) {
398 throw new NullPointerException("url cannot be null");
399 }
400 if (requestor == null) {
401 throw new NullPointerException("requestor cannot be null");
402 }
403
404 Collection<ShpFilesLocker> threadLockers = getCurrentThreadLockers();
405 boolean removed = threadLockers.remove(new ShpFilesLocker(url, requestor));
406 if (!removed) {
407 throw new IllegalArgumentException(
408 "Expected requestor "
409 + requestor
410 + " to have locked the url but it does not hold the lock for the URL");
411 }
412 if(threadLockers.size() == 0)
413 lockers.remove(Thread.currentThread());
414 readWriteLock.readLock().unlock();
415 }
416
417 /**
418 * Acquire a File for read and write purposes.
419 * <p> It is recommended that get*Stream or
420 * get*Channel methods are used when reading or writing to the file is
421 * desired.
422 * </p>
423 *
424 * @see #getInputStream(ShpFileType, FileReader)
425 * @see #getReadChannel(ShpFileType, FileReader)
426 * @see #getWriteChannel(ShpFileType, FileReader)
427
428 *
429 * @param type
430 * the type of the file desired.
431 * @param requestor
432 * the object that is requesting the File. The same object
433 * must release the lock and is also used for debugging.
434 * @return the File to the file of the type requested
435 */
436 @Override
437 public File acquireWriteFile(ShpFileType type,
438 FileWriter requestor) {
439 if(!isLocalTab() ){
440 throw new IllegalStateException("This method only applies if the files are local");
441 }
442 URL url = acquireWrite(type, requestor);
443 return DataUtilities.urlToFile(url);
444 }
445 /**
446 * Acquire a URL for read and write purposes.
447 * <p> It is recommended that get*Stream or
448 * get*Channel methods are used when reading or writing to the file is
449 * desired.
450 * </p>
451 *
452 * @see #getInputStream(ShpFileType, FileReader)
453 * @see #getReadChannel(ShpFileType, FileReader)
454 * @see #getWriteChannel(ShpFileType, FileReader)
455
456 *
457 * @param type
458 * the type of the file desired.
459 * @param requestor
460 * the object that is requesting the URL. The same object
461 * must release the lock and is also used for debugging.
462 * @return the URL to the file of the type requested
463 */
464 @Override
465 public URL acquireWrite(ShpFileType type, FileWriter requestor) {
466 URL url = urls.get(type);
467 if (url == null) {
468 return null;
469 }
470
471 // we need to give up all read locks before getting the write one
472 Collection<ShpFilesLocker> threadLockers = getCurrentThreadLockers();
473 relinquishReadLocks(threadLockers);
474 readWriteLock.writeLock().lock();
475 threadLockers.add(new ShpFilesLocker(url, requestor));
476 mapCache.cleanFileCache(url);
477 return url;
478 }
479
480 /**
481 * Tries to acquire a URL for read/write purposes. Returns null if the
482 * acquire failed or if the file does not exist
483 * <p> It is recommended that get*Stream or
484 * get*Channel methods are used when reading or writing to the file is
485 * desired.
486 * </p>
487 *
488 * @see #getInputStream(ShpFileType, FileReader)
489 * @see #getReadChannel(ShpFileType, FileReader)
490 * @see #getWriteChannel(ShpFileType, FileReader)
491
492 *
493 * @param type
494 * the type of the file desired.
495 * @param requestor
496 * the object that is requesting the URL. The same object
497 * must release the lock and is also used for debugging.
498 * @return A result object containing the URL or the reason for the failure.
499 */
500 @Override
501 public Result<URL, State> tryAcquireWrite(ShpFileType type,
502 FileWriter requestor) {
503
504 URL url = urls.get(type);
505 if (url == null) {
506 return new Result<>(null, State.NOT_EXIST);
507 }
508
509 Collection<ShpFilesLocker> threadLockers = getCurrentThreadLockers();
510 boolean locked = readWriteLock.writeLock().tryLock();
511 if (!locked && threadLockers.size() > 1) {
512 // hum, it may be be because we are holding a read lock
513 relinquishReadLocks(threadLockers);
514 locked = readWriteLock.writeLock().tryLock();
515 if(locked == false) {
516 regainReadLocks(threadLockers);
517 return new Result<>(null, State.LOCKED);
518 }
519 }
520
521 threadLockers.add(new ShpFilesLocker(url, requestor));
522 return new Result<>(url, State.GOOD);
523 }
524
525 /**
526 * Unlocks a read lock. The file and requestor must be the the same as the
527 * one of the lockers.
528 *
529 * @param file
530 * file that was locked
531 * @param requestor
532 * the class that requested the file
533 */
534 @Override
535 public void unlockWrite(File file, FileWriter requestor) {
536 Collection<URL> allURLS = urls.values();
537 for (URL url : allURLS) {
538 if (DataUtilities.urlToFile(url).equals(file)) {
539 unlockWrite(url, requestor);
540 }
541 }
542 }
543
544 /**
545 * Unlocks a read lock. The requestor must be have previously obtained a
546 * lock for the url.
547 *
548 *
549 * @param url
550 * url that was locked
551 * @param requestor
552 * the class that requested the url
553 */
554 @Override
555 public void unlockWrite(URL url, FileWriter requestor) {
556 if (url == null) {
557 throw new NullPointerException("url cannot be null");
558 }
559 if (requestor == null) {
560 throw new NullPointerException("requestor cannot be null");
561 }
562 Collection<ShpFilesLocker> threadLockers = getCurrentThreadLockers();
563 boolean removed = threadLockers.remove(new ShpFilesLocker(url, requestor));
564 if (!removed) {
565 throw new IllegalArgumentException(
566 "Expected requestor "
567 + requestor
568 + " to have locked the url but it does not hold the lock for the URL");
569 }
570
571 if(threadLockers.size() == 0) {
572 lockers.remove(Thread.currentThread());
573 } else {
574 // get back read locks before giving up the write one
575 regainReadLocks(threadLockers);
576 }
577 readWriteLock.writeLock().unlock();
578 }
579
580 /**
581 * Returns the list of lockers attached to a given thread, or creates it if missing
582 * @return
583 */
584 private Collection<ShpFilesLocker> getCurrentThreadLockers() {
585 Collection<ShpFilesLocker> threadLockers = lockers.get(Thread.currentThread());
586 if(threadLockers == null) {
587 threadLockers = new ArrayList<>();
588 lockers.put(Thread.currentThread(), threadLockers);
589 }
590 return threadLockers;
591 }
592
593 /**
594 * Gives up all read locks in preparation for lock upgade
595 * @param threadLockers
596 */
597 private void relinquishReadLocks(Collection<ShpFilesLocker> threadLockers) {
598 for (ShpFilesLocker shpFilesLocker : threadLockers) {
599 if(shpFilesLocker.reader != null && !shpFilesLocker.upgraded) {
600 readWriteLock.readLock().unlock();
601 shpFilesLocker.upgraded = true;
602 }
603 }
604 }
605
606 /**
607 * Re-takes the read locks in preparation for lock downgrade
608 * @param threadLockers
609 */
610 private void regainReadLocks(Collection<ShpFilesLocker> threadLockers) {
611 for (ShpFilesLocker shpFilesLocker : threadLockers) {
612 if(shpFilesLocker.reader != null && shpFilesLocker.upgraded) {
613 readWriteLock.readLock().lock();
614 shpFilesLocker.upgraded = false;
615 }
616 }
617 }
618
619 /**
620 * Determine if the location of this shapefile is local or remote.
621 *
622 * @return true if local, false if remote
623 */
624 public boolean isLocalTab() {
625 return urls.get(ShpFileType.SHP).toExternalForm().toLowerCase().startsWith("file:");
626 }
627
628 /**
629 * Delete all the shapefile files. If the files are not local or the one
630 * files cannot be deleted return false.
631 */
632 @Override
633 public boolean delete() {
634 BasicShpFileWriter requestor = new BasicShpFileWriter("ShpFiles for deleting all files");
635 URL writeLockURL = acquireWrite(SHP, requestor);
636 boolean retVal = true;
637 try {
638 if (isLocalTab()) {
639 Collection<URL> values = urls.values();
640 for (URL url : values) {
641 File f = DataUtilities.urlToFile(url);
642 if (!f.delete()) {
643 retVal = false;
644 }
645 }
646 } else {
647 retVal = false;
648 }
649 } finally {
650 unlockWrite(writeLockURL, requestor);
651 }
652 return retVal;
653 }
654
655 /**
656 * Opens a input stream for the indicated file. A read lock is requested at
657 * the method call and released on close.
658 *
659 * @param type
660 * the type of file to open the stream to.
661 * @param requestor
662 * the object requesting the stream
663 * @return an input stream
664 *
665 * @throws IOException
666 * if a problem occurred opening the stream.
667 */
668 @Override
669 public InputStream getInputStream(ShpFileType type,
670 final FileReader requestor) throws IOException {
671 final URL url = acquireRead(type, requestor);
672
673 try {
674 FilterInputStream input = new FilterInputStream(url.openStream()) {
675
676 private volatile boolean closed = false;
677
678 @Override
679 public void close() throws IOException {
680 try {
681 super.close();
682 } finally {
683 if (!closed) {
684 closed = true;
685 unlockRead(url, requestor);
686 }
687 }
688 }
689
690 };
691 return input;
692 } catch (Throwable e) {
693 unlockRead(url, requestor);
694 if (e instanceof IOException) {
695 throw (IOException) e;
696 } else if( e instanceof RuntimeException) {
697 throw (RuntimeException) e;
698 } else if( e instanceof Error ) {
699 throw (Error) e;
700 } else {
701 throw new RuntimeException(e);
702 }
703 }
704 }
705
706 /**
707 * Opens a output stream for the indicated file. A write lock is requested at
708 * the method call and released on close.
709 *
710 * @param type
711 * the type of file to open the stream to.
712 * @param requestor
713 * the object requesting the stream
714 * @return an output stream
715 *
716 * @throws IOException
717 * if a problem occurred opening the stream.
718 */
719 @SuppressWarnings("resource")
720 @Override
721 public OutputStream getOutputStream(ShpFileType type,
722 final FileWriter requestor) throws IOException {
723 final URL url = acquireWrite(type, requestor);
724
725 try {
726
727 OutputStream out;
728 if (isLocalTab()) {
729 File file = DataUtilities.urlToFile(url);
730 out = new FileOutputStream(file);
731 } else {
732 URLConnection connection = url.openConnection();
733 connection.setDoOutput(true);
734 out = connection.getOutputStream();
735 }
736
737 FilterOutputStream output = new FilterOutputStream(out) {
738
739 private volatile boolean closed = false;
740
741 @Override
742 public void close() throws IOException {
743 try {
744 super.close();
745 } finally {
746 if (!closed) {
747 closed = true;
748 unlockWrite(url, requestor);
749 }
750 }
751 }
752
753 };
754
755 return output;
756 } catch (Throwable e) {
757 unlockWrite(url, requestor);
758 if (e instanceof IOException) {
759 throw (IOException) e;
760 } else if (e instanceof RuntimeException) {
761 throw (RuntimeException) e;
762 } else if (e instanceof Error) {
763 throw (Error) e;
764 } else {
765 throw new RuntimeException(e);
766 }
767 }
768 }
769
770 /**
771 * Obtain a ReadableByteChannel from the given URL. If the url protocol is
772 * file, a FileChannel will be returned. Otherwise a generic channel will be
773 * obtained from the urls input stream.
774 * <p>
775 * A read lock is obtained when this method is called and released when the
776 * channel is closed.
777 * </p>
778 *
779 * @param type
780 * the type of file to open the channel to.
781 * @param requestor
782 * the object requesting the channel
783 *
784 */
785 @Override
786 public ReadableByteChannel getReadChannel(ShpFileType type,
787 FileReader requestor) throws IOException {
788 URL url = acquireRead(type, requestor);
789 ReadableByteChannel channel = null;
790 try {
791 if (isLocalTab()) {
792
793 File file = DataUtilities.urlToFile(url);
794
795 @SuppressWarnings("resource")
796 RandomAccessFile raf = new RandomAccessFile(file, "r");
797 channel = new FileChannelDecorator(raf.getChannel(), this, url,
798 requestor);
799
800 } else {
801 InputStream in = url.openConnection().getInputStream();
802 channel = new ReadableByteChannelDecorator(Channels
803 .newChannel(in), this, url, requestor);
804 }
805 } catch (Throwable e) {
806 unlockRead(url, requestor);
807 if (e instanceof IOException) {
808 throw (IOException) e;
809 } else if (e instanceof RuntimeException) {
810 throw (RuntimeException) e;
811 } else if (e instanceof Error) {
812 throw (Error) e;
813 } else {
814 throw new RuntimeException(e);
815 }
816 }
817 return channel;
818 }
819
820 /**
821 * Obtain a WritableByteChannel from the given URL. If the url protocol is
822 * file, a FileChannel will be returned. Currently, this method will return
823 * a generic channel for remote urls, however both shape and dbf writing can
824 * only occur with a local FileChannel channel.
825 *
826 * <p>
827 * A write lock is obtained when this method is called and released when the
828 * channel is closed.
829 * </p>
830 *
831 *
832 * @param type
833 * the type of file to open the stream to.
834 * @param requestor
835 * the object requesting the stream
836 *
837 * @return a WritableByteChannel for the provided file type
838 *
839 * @throws IOException
840 * if there is an error opening the stream
841 */
842 @Override
843 public WritableByteChannel getWriteChannel(ShpFileType type,
844 FileWriter requestor) throws IOException {
845
846 URL url = acquireWrite(type, requestor);
847
848 try {
849 WritableByteChannel channel;
850 if (isLocalTab()) {
851
852 File file = DataUtilities.urlToFile(url);
853
854 @SuppressWarnings("resource")
855 RandomAccessFile raf = new RandomAccessFile(file, "rw");
856 channel = new FileChannelDecorator(raf.getChannel(), this, url,
857 requestor);
858
859 ((FileChannel) channel).lock();
860
861 } else {
862 OutputStream out = url.openConnection().getOutputStream();
863 channel = new WritableByteChannelDecorator(Channels
864 .newChannel(out), this, url, requestor);
865 }
866
867 return channel;
868 } catch (Throwable e) {
869 unlockWrite(url, requestor);
870 if (e instanceof IOException) {
871 throw (IOException) e;
872 } else if (e instanceof RuntimeException) {
873 throw (RuntimeException) e;
874 } else if (e instanceof Error) {
875 throw (Error) e;
876 } else {
877 throw new RuntimeException(e);
878 }
879 }
880 }
881
882 /**
883 * Obtains a Storage file for the type indicated. An id is provided so that
884 * the same file can be obtained at a later time with just the id
885 *
886 * @param type
887 * the type of file to create and return
888 *
889 * @return StorageFile
890 * @throws IOException
891 * if temporary files cannot be created
892 */
893 @Override
894 public StorageFile getStorageFile(ShpFileType type) throws IOException {
895 String baseName = getTypeName();
896 if (baseName.length() < 3) { // min prefix length for createTempFile
897 baseName = baseName + "___".substring(0, 3 - baseName.length());
898 }
899 File tmp = File.createTempFile(baseName, type.extensionWithPeriod);
900 return new StorageFile(this, tmp, type);
901 }
902
903 @Override
904 public String getTypeName() {
905 String path = SHP.toBase(urls.get(SHP));
906 int slash = Math.max(0, path.lastIndexOf('/') + 1);
907 int dot = path.indexOf('.', slash);
908
909 if (dot < 0) {
910 dot = path.length();
911 }
912
913 return path.substring(slash, dot);
914 }
915
916 /**
917 * Internal method that the file channel decorators will call to allow reuse of the memory mapped buffers
918 * @param wrapped
919 * @param url
920 * @param mode
921 * @param position
922 * @param size
923 * @return
924 * @throws IOException
925 */
926 @Override
927 MappedByteBuffer map(FileChannel wrapped, URL url, MapMode mode, long position, long size) throws IOException {
928 if(memoryMapCacheEnabled) {
929 return mapCache.map(wrapped, url, mode, position, size);
930 } else {
931 return wrapped.map(mode, position, size);
932 }
933 }
934
935 /**
936 * Returns the status of the memory map cache. When enabled the memory mapped portions of the files are cached and shared
937 * (giving each thread a clone of it)
938 * @param memoryMapCacheEnabled
939 */
940 @Override
941 public boolean isMemoryMapCacheEnabled() {
942 return memoryMapCacheEnabled;
943 }
944
945 /**
946 * Enables the memory map cache. When enabled the memory mapped portions of the files are cached and shared
947 * (giving each thread a clone of it)
948 * @param memoryMapCacheEnabled
949 */
950 @Override
951 public void setMemoryMapCacheEnabled(boolean memoryMapCacheEnabled) {
952 this.memoryMapCacheEnabled = memoryMapCacheEnabled;
953 if (!memoryMapCacheEnabled) {
954 mapCache.clean();
955 }
956 }
957
958 /**
959 * Returns true if the file exists. Throws an exception if the file is not
960 * local.
961 *
962 * @param fileType
963 * the type of file to check existance for.
964 *
965 * @return true if the file exists.
966 *
967 * @throws IllegalArgumentException
968 * if the files are not local.
969 */
970 public boolean existsTab(ShpFileType fileType) throws IllegalArgumentException {
971 if (!isLocalTab()) {
972 throw new IllegalArgumentException(
973 "This method only makes sense if the files are local");
974 }
975 URL url = urls.get(fileType);
976 if (url == null) {
977 return false;
978 }
979
980 File file = DataUtilities.urlToFile(url);
981 return file.exists();
982 }
983}
Note: See TracBrowser for help on using the repository browser.