1 package au.gov.amsa.ihs.reader;
2
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.of;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.Enumeration;
10 import java.util.Map;
11 import java.util.zip.ZipEntry;
12 import java.util.zip.ZipFile;
13
14 import org.joda.time.DateTime;
15
16 import com.google.common.base.Optional;
17
18 import au.gov.amsa.ihs.model.Ship;
19 import rx.Observable;
20 import rx.Observable.OnSubscribe;
21 import rx.Subscriber;
22
23 public class IhsReader {
24
25 public Observable<Map<String, String>> from(InputStream is, String parentElementName) {
26 return Observable.just(is).lift(new OperatorIhsReader(parentElementName));
27 }
28
29 public static Observable<Map<String, String>> fromZip(File file) {
30 return shipDataFilesAsInputStreamFromZip(file).lift(new OperatorIhsReader("ShipData"));
31 }
32
33 public static Observable<Map<String, Map<String, String>>> fromZipAsMapByImo(File file) {
34 return fromZip(file).toMap(map -> map.get(Key.LRIMOShipNo.name()));
35 }
36
37 public static Observable<Map<String, Map<String, String>>> fromZipAsMapByMmsi(File file) {
38 return fromZip(file)
39
40 .filter(map -> map.get(Key.MaritimeMobileServiceIdentityMMSINumber.name()) != null)
41
42 .toMap(map -> map.get(Key.MaritimeMobileServiceIdentityMMSINumber.name()));
43 }
44
45 private static Observable<InputStream> shipDataFilesAsInputStreamFromZip(final File file) {
46 return Observable.create(new OnSubscribe<InputStream>() {
47
48 @Override
49 public void call(Subscriber<? super InputStream> subscriber) {
50
51 ZipFile zip = null;
52 try {
53 zip = new ZipFile(file);
54 Enumeration<? extends ZipEntry> en = zip.entries();
55 while (en.hasMoreElements() && !subscriber.isUnsubscribed()) {
56 ZipEntry entry = en.nextElement();
57 if (entry.getName().startsWith("ShipData")
58 && entry.getName().endsWith(".xml")) {
59 InputStream is = zip.getInputStream(entry);
60 System.out.println(entry.getName());
61 subscriber.onNext(is);
62 }
63 }
64 subscriber.onCompleted();
65 } catch (Exception e) {
66 subscriber.onError(e);
67 } finally {
68 try {
69 if (zip != null)
70 zip.close();
71 } catch (IOException e) {
72
73 }
74 }
75 }
76
77 });
78 }
79
80 public static Ship toShip(Map<String, String> values) {
81 return new ShipCreator(values).buildShip();
82 }
83
84 private static class ShipCreator {
85 private final Map<String, String> values;
86
87 ShipCreator(Map<String, String> values) {
88 this.values = values;
89 }
90
91 Ship buildShip() {
92 return Ship.builder().classificationSocietyCode(value("ClassificationSocietyCode"))
93 .countryOfBuildCode(value("CountryOfBuildCode"))
94 .yearOfBuild(toYearOfBuild(value("DateOfBuild")))
95 .monthOfBuild(toMonthOfBuild(value("DateOfBuild"))).flagCode(value("FlagCode"))
96 .grossTonnage(toLong(value("GrossTonnage")))
97 .groupBeneficialOwnerCompanyCode(value("GroupBeneficialOwnerCompanyCode"))
98 .groupBeneficialOwnerCountryOfDomicileCode(
99 value("GroupBeneficialOwnerCountryofDomicileCode"))
100 .imo(value("LRIMOShipNo").get())
101 .mmsi(value("MaritimeMobileServiceIdentityMMSINumber"))
102 .type2(value("ShiptypeLevel2")).type3(value("ShiptypeLevel3"))
103 .type4(value("ShiptypeLevel4")).type5(value("ShiptypeLevel5"))
104 .deadweightTonnage(toFloat(value("FormulaDWT"))).statCode5(value("StatCode5"))
105 .lengthOverallMetres(toFloat(value("LengthOverallLOA")))
106 .breadthMetres(toFloat(value("Breadth")))
107 .displacementTonnage(toFloat(value("Displacement")))
108 .draughtMetres(toFloat(value("Draught"))).speedKnots(toFloat(value("Speed")))
109 .lastUpdateTime(toDateTime(value("LastUpdateDate"))).name(value("ShipName"))
110 .shipBuilderCompanyCode(value("ShipbuilderCompanyCode"))
111
112 .build();
113 }
114
115 private Optional<String> value(String name) {
116 String val = values.get(name);
117 if (val == null)
118 return absent();
119 else if (val.trim().length() == 0)
120 return absent();
121 else
122 return Optional.fromNullable(val.trim());
123 }
124
125 }
126
127 private static Optional<DateTime> toDateTime(Optional<String> value) {
128 if (!value.isPresent())
129 return absent();
130 else {
131 try {
132 return of(DateTime.parse(value.get()));
133 } catch (RuntimeException e) {
134 return absent();
135 }
136 }
137 }
138
139 private static Optional<Integer> toYearOfBuild(Optional<String> value) {
140 if (!value.isPresent())
141 return absent();
142 else {
143 try {
144 return of(Integer.parseInt(value.get().substring(0, 4)));
145 } catch (RuntimeException e) {
146 return absent();
147 }
148 }
149 }
150
151 private static Optional<Integer> toMonthOfBuild(Optional<String> value) {
152 if (!value.isPresent())
153 return absent();
154 else {
155 int month = Integer.parseInt(value.get().substring(4, 6));
156 if (month > 0)
157 return of(month);
158 else
159 return absent();
160 }
161 }
162
163 private static Optional<Long> toLong(Optional<String> value) {
164 if (!value.isPresent())
165 return Optional.absent();
166 else {
167 return Optional.of(Long.parseLong(value.get()));
168 }
169 }
170
171 private static Optional<Float> toFloat(Optional<String> value) {
172 if (!value.isPresent())
173 return Optional.absent();
174 else {
175 return Optional.of(Float.parseFloat(value.get()));
176 }
177 }
178
179 }