001/* 002 * $Id: LocalAddeEntry.java,v 1.25 2011/03/24 16:06:34 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030package edu.wisc.ssec.mcidasv.servermanager; 031 032import java.util.Collections; 033import java.util.List; 034import java.util.Map; 035import java.util.Random; 036 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType; 041 042/** 043 * 044 * 045 * 046 */ 047public class LocalAddeEntry implements AddeEntry { 048 049 static final Logger logger = LoggerFactory.getLogger(LocalAddeEntry.class); 050 051 /** Represents a {@literal "bad"} local ADDE entry. */ 052 // seriously, don't use null unless you REALLY need it. 053 public static final LocalAddeEntry INVALID_ENTRY = new Builder("INVALID", "INVALID", "/dev/null", AddeFormat.INVALID).build(); 054 055 public static final List<LocalAddeEntry> INVALID_ENTRIES = Collections.singletonList(INVALID_ENTRY); 056 057 /** */ 058 private static final String CYGWIN_PREFIX = "/cygdrive/"; 059 060 /** */ 061 private static final int CYGWIN_PREFIX_LEN = CYGWIN_PREFIX.length(); 062 063 /** */ 064 private EntryStatus entryStatus = EntryStatus.INVALID; 065 066 /** N1 */ 067 private final String group; 068 069 /** N2 */ 070 private final String descriptor; 071 072 /** RT */ 073 private final boolean realtime; 074 075 /** MCV */ 076 private final AddeFormat format; 077 078 /** R1 */ 079 private final String start; 080 081 /** R2 */ 082 private final String end; 083 084 /** MASK */ 085 private final String fileMask; 086 087 /** C */ 088 private final String name; 089 090 091 private String asStringId; 092 093 public enum ServerName { 094 AREA, AMSR, AMRR, GINI, FSDX, OMTP, LV1B, MODS, MODX, MOD4, MOD8, 095 MODR, MSGT, MTST, SMIN, TMIN, INVALID; 096 } 097 098 /** 099 * The various kinds of local ADDE data understood by McIDAS-V, along with 100 * some helpful metadata. 101 * 102 * <p><ul> 103 * <li>{@literal "Human readable"} format names ({@link #friendlyName}).</li> 104 * <li>Optional tooltip description ({@link #tooltip}).</li> 105 * <li>Type of data ({@link #type}).</li> 106 * <li>File naming pattern {@link #fileFilter}.</li> 107 * </ul> 108 * 109 * <p>None of {@code AddeFormat}'s fields should contain {@code null}. 110 */ 111 public enum AddeFormat { 112 INVALID(ServerName.INVALID, "", "", EntryType.INVALID), 113 MCIDAS_AREA(ServerName.AREA, "McIDAS AREA"), 114 AMSRE_L1B(ServerName.AMSR, "AMSR-E L 1b", "AMSR-E Level 1b"), 115 AMSRE_RAIN_PRODUCT(ServerName.AMRR, "AMSR-E Rain Product"), 116 GINI(ServerName.GINI, "GINI"), 117 LRIT_GOES9(ServerName.FSDX, "LRIT GOES-9", "EUMETCast LRIT GOES-9"), 118 LRIT_GOES10(ServerName.FSDX, "LRIT GOES-10", "EUMETCast LRIT GOES-10"), 119 LRIT_GOES11(ServerName.FSDX, "LRIT GOES-11", "EUMETCast LRIT GOES-11"), 120 LRIT_GOES12(ServerName.FSDX, "LRIT GOES-12", "EUMETCast LRIT GOES-12"), 121 LRIT_MET5(ServerName.FSDX, "LRIT MET-5", "EUMETCast LRIT MET-5"), 122 LRIT_MET7(ServerName.FSDX, "LRIT MET-7", "EUMETCast LRIT MET-7"), 123 LRIT_MTSAT1R(ServerName.FSDX, "LRIT MTSAT-1R", "EUMETCast LRIT MTSAT-1R"), 124 METEOSAT_OPENMTP(ServerName.OMTP, "Meteosat OpenMTP"), 125 METOP_AVHRR_L1B(ServerName.LV1B, "Metop AVHRR L 1b", "Metop AVHRR Level 1b"), 126 MODIS_L1B_MOD02(ServerName.MODS, "MODIS MOD 02 - Level-1B Calibrated Geolocated Radiances", "MODIS Level 1b"), 127 MODIS_L2_MOD06(ServerName.MODX, "MODIS MOD 06 - Cloud Product", "MODIS Level 2 (Cloud Top Properties)"), 128 MODIS_L2_MOD07(ServerName.MODX, "MODIS MOD 07 - Atmospheric Profiles", "MODIS Level 2 (Atmospheric Profile)"), 129 MODIS_L2_MOD35(ServerName.MODX, "MODIS MOD 35 - Cloud Mask", "MODIS Level 2 (Cloud Mask)"), 130 MODIS_L2_MOD04(ServerName.MOD4, "MODIS MOD 04 - Aerosol Product", "MODIS Level 2 (Aerosol)"), 131 MODIS_L2_MOD28(ServerName.MOD8, "MODIS MOD 28 - Sea Surface Temperature", "MODIS Level 2 (Sea Surface Temperature)"), 132 MODIS_L2_MODR(ServerName.MODR, "MODIS MOD R - Corrected Reflectance", "MODIS Level 2 (Corrected Reflectance)"), 133 MSG_HRIT_FD(ServerName.MSGT, "MSG HRIT FD", "MSG HRIT (Full Disk)"), 134 MSG_HRIT_HRV(ServerName.MSGT, "MSG HRIT HRV", "MSG HRIT (High Resolution Visible)"), 135 MTSAT_HRIT(ServerName.MTST, "MTSAT HRIT"), 136 NOAA_AVHRR_L1B(ServerName.LV1B, "NOAA AVHRR L 1b", "NOAA AVHRR Level 1b"), 137 SSMI(ServerName.SMIN, "SSMI", "Terrascan netCDF (SMIN)"), 138 TRMM(ServerName.TMIN, "TRMM", "Terrascan netCDF (TMIN)"); 139 140 /** Name of the server (should be four characters). */ 141 private final ServerName servName; 142 143 /** {@literal "Human readable"} format name. This is returned by {@link #toString()}. */ 144 private final String friendlyName; 145 146 /** Description of the format. */ 147 private final String tooltip; 148 149 /** Data type. Corresponds to {@code TYPE} in {@literal "RESOLV.SRV"}. */ 150 private final EntryType type; 151 152 /** 153 * Filename pattern used when listing files in a directory. 154 * If {@link #servName} is {@link ServerName#MSGT} then 155 * {@literal "*PRO*"} is used, otherwise {@literal "*"}. 156 */ 157 private final String fileFilter; 158 159 /** 160 * Builds an {@literal "ADDE format"} and its associated metadata in 161 * a typesafe way. 162 * 163 * @param servName {@link ServerName} that McIDAS-X uses for this format. 164 * @param friendlyName {@literal "Human readable"} name of the format; returned by {@link #toString()}. 165 * @param tooltip If non-empty, this is used as a tooltip in the local entry editor. 166 * @param type Only use {@link EntryType#IMAGE} for the time being? 167 */ 168 private AddeFormat(final ServerName servName, final String friendlyName, final String tooltip, final EntryType type) { 169 this.servName = servName; 170 this.friendlyName = friendlyName; 171 this.tooltip = tooltip; 172 this.type = type; 173 this.fileFilter = (servName != ServerName.MSGT) ? "*" : "*PRO*"; 174 } 175 176 /** 177 * Builds an {@literal "ADDE Format"} <b>without</b> a tooltip. 178 */ 179 private AddeFormat(final ServerName servName, final String friendlyName) { 180 this(servName, friendlyName, "", EntryType.IMAGE); 181 } 182 183 /** 184 * Builds an {@literal "ADDE Format"} <b>with</b> a tooltip. 185 */ 186 private AddeFormat(final ServerName servName, final String friendlyName, final String tooltip) { 187 this(servName, friendlyName, tooltip, EntryType.IMAGE); 188 } 189 190 public ServerName getServerName() { return servName; } 191 public String getTooltip() { return tooltip; } 192 public EntryType getType() { return type; } 193 public String getFileFilter() { return fileFilter; } 194 @Override public String toString() { return friendlyName; } 195 } 196 197 private LocalAddeEntry(final Builder builder) { 198 this.group = builder.group; 199 this.descriptor = builder.descriptor; 200 this.realtime = builder.realtime; 201 this.format = builder.format; 202 this.fileMask = builder.mask; 203 this.name = builder.name; 204 this.start = builder.start; 205 this.end = builder.end; 206 this.entryStatus = builder.status; 207 logger.debug("created local: {}", this); 208 } 209 210 @Override public AddeAccount getAccount() { 211 return RemoteAddeEntry.DEFAULT_ACCOUNT; 212 } 213 214 @Override public String getAddress() { 215 return "localhost"; 216 } 217 218 @Override public EntrySource getEntrySource() { 219 return EntrySource.USER; 220 } 221 222 @Override public EntryStatus getEntryStatus() { 223 return entryStatus; 224 } 225 226 @Override public String getEntryText() { 227 return "localhost/"+getGroup(); 228 } 229 230 @Override public EntryType getEntryType() { 231 return format.getType(); 232 } 233 234 @Override public EntryValidity getEntryValidity() { 235 return (isValid()) ? EntryValidity.VERIFIED : EntryValidity.INVALID; 236 } 237 238 // TODO(jon): fix this noop 239 @Override public String getEntryAlias() { 240 return ""; 241 } 242 243 // TODO(jon): fix this noop 244 @Override public void setEntryAlias(final String newAlias) { 245 if (newAlias == null) { 246 throw new NullPointerException("Null aliases are not allowable."); 247 } 248 } 249 250 @Override public void setEntryStatus(EntryStatus newStatus) { 251 entryStatus = newStatus; 252 } 253 254 @Override public String getGroup() { 255 return group; 256 } 257 258 public String getDescriptor() { 259 return descriptor; 260 } 261 262 public AddeFormat getFormat() { 263 return format; 264 } 265 266 public String getMask() { 267 return fileMask; 268 } 269 270 public String getFileMask() { 271 return fileMask; 272 } 273 274 @Override public String getName() { 275 return name; 276 } 277 278 public boolean getRealtime() { 279 return realtime; 280 } 281 282 public String getStart() { 283 return start; 284 } 285 286 public String getEnd() { 287 return end; 288 } 289 290 public boolean isValid() { 291 if ((group.length() == 0) || (descriptor.length() == 0) || (name.length() == 0)) { 292 return false; 293 } 294 return true; 295 } 296 297 public String getRealtimeAsString() { 298 if (realtime) { 299 return "Y"; 300 } 301 return "N"; 302 } 303 304 @Override public int hashCode() { 305 final int prime = 31; 306 int result = 1; 307 result = prime * result 308 + ((fileMask == null) ? 0 : fileMask.hashCode()); 309 result = prime * result + ((format == null) ? 0 : format.hashCode()); 310 result = prime * result + ((group == null) ? 0 : group.hashCode()); 311 result = prime * result + ((name == null) ? 0 : name.hashCode()); 312 return result; 313 } 314 315 @Override public boolean equals(Object obj) { 316 if (this == obj) { 317 return true; 318 } 319 if (obj == null) { 320 return false; 321 } 322 if (!(obj instanceof LocalAddeEntry)) { 323 return false; 324 } 325 LocalAddeEntry other = (LocalAddeEntry) obj; 326 if (fileMask == null) { 327 if (other.fileMask != null) { 328 return false; 329 } 330 } else if (!fileMask.equals(other.fileMask)) { 331 return false; 332 } 333 if (format == null) { 334 if (other.format != null) { 335 return false; 336 } 337 } else if (!format.equals(other.format)) { 338 return false; 339 } 340 if (group == null) { 341 if (other.group != null) { 342 return false; 343 } 344 } else if (!group.equals(other.group)) { 345 return false; 346 } 347 if (name == null) { 348 if (other.name != null) { 349 return false; 350 } 351 } else if (!name.equals(other.name)) { 352 return false; 353 } 354 return true; 355 } 356 357 @Override public String asStringId() { 358 if (asStringId == null) { 359 asStringId = "localhost!"+group+'!'+EntryType.IMAGE.name()+'!'+name; 360 } 361 return asStringId; 362 } 363 364 @Override public String toString() { 365 return String.format( 366 "[LocalAddeEntry@%x: name=%s, group=%s, fileMask=\"%s\", descriptor=%s, serverName=%s, format=%s, description=%s, type=%s, status=%s]", 367 hashCode(), name, group, fileMask, descriptor, format.getServerName().name(), format.name(), format.getTooltip(), format.getType(), entryStatus.name()); 368 369 } 370 371 public static class Builder { 372 private static final Random random = new Random(); 373 374 // required 375 private final String group; 376 private final String name; 377 private final AddeFormat format; 378 private final String mask; 379 380 // optional 381 private String descriptor = "ENTRY"+random.nextInt(999999); 382 private boolean realtime = false; 383 private String start = "1"; 384 private String end = "999999"; 385 private EntryStatus status = EntryStatus.INVALID; 386 387 private EntryType type = EntryType.IMAGE; 388 private String kind = "NOT_SET"; 389 private ServerName safeKind = ServerName.INVALID; 390 391 public Builder(final Map<String, String> map) { 392 if (!map.containsKey("C") || !map.containsKey("N1") || !map.containsKey("MASK") || !map.containsKey("MCV")) { 393 throw new IllegalArgumentException(""); 394 } 395 396 this.name = map.get("C"); 397 this.group = map.get("N1"); 398 this.mask = map.get("MASK"); 399 this.format = EntryTransforms.strToAddeFormat(map.get("MCV")); 400 401 descriptor(map.get("N2")); 402 type(EntryTransforms.strToEntryType(map.get("TYPE"))); 403 kind(map.get("K").toUpperCase()); 404 realtime(map.get("RT")); 405 start(map.get("R1")); 406 end(map.get("R2")); 407 } 408 409 410 public Builder(final String name, final String group, final String mask, final AddeFormat format) { 411 this.name = name; 412 this.group = group; 413 this.mask = mask; 414 this.format = format; 415 } 416 417 public Builder descriptor(final String descriptor) { 418 if (descriptor != null) { 419 this.descriptor = descriptor; 420 } 421 return this; 422 } 423 424 // looks like mcidasx understands ("Y"/"N"/"A") 425 // should probably ignore case and accept "YES"/"NO"/"ARCHIVE" 426 // in addition to the normal boolean conversion from String 427 public Builder realtime(final String realtimeAsStr) { 428 if (realtimeAsStr == null) { 429 return this; 430 } 431 432 if ("Y".equalsIgnoreCase(realtimeAsStr) || "YES".equalsIgnoreCase(realtimeAsStr)) { 433 this.realtime = true; 434 } else { 435 this.realtime = Boolean.valueOf(realtimeAsStr); 436 } 437 return this; 438 } 439 440 public Builder realtime(final boolean realtime) { 441 this.realtime = realtime; 442 return this; 443 } 444 445 // my assumption is that if "format" is known, you can infer "type" 446 public Builder type(final EntryType type) { 447 if (type != null) { 448 this.type = type; 449 } 450 return this; 451 } 452 453 // my assumption is that if "format" is known, you can infer "kind" 454 public Builder kind(final String kind) { 455 if (kind == null) { 456 return this; 457 } 458 459 this.kind = kind; 460 try { 461 this.safeKind = ServerName.valueOf(kind); 462 } catch (IllegalArgumentException e) { 463 this.safeKind = ServerName.INVALID; 464 } 465 return this; 466 } 467 468 public Builder start(final String start) { 469 if (start != null) { 470 this.start = start; 471 } 472 return this; 473 } 474 475 public Builder end(final String end) { 476 if (end != null) { 477 this.end = end; 478 } 479 return this; 480 } 481 482 public Builder range(final String start, final String end) { 483 if (start != null && end != null) { 484 this.start = start; 485 this.end = end; 486 } 487 return this; 488 } 489 490 public Builder status(final String status) { 491 if (status != null && status.length() > 0) { 492 this.status = EntryTransforms.strToEntryStatus(status); 493 } 494 return this; 495 } 496 497 public Builder status(final EntryStatus status) { 498 if (status != null) { 499 this.status = status; 500 } 501 return this; 502 } 503 504 public LocalAddeEntry build() { 505// if (format.getType() != type || format.getServerName() != safeKind || safeKind == ServerName.INVALID) 506// System.err.println("oddity: name="+name+" mask="+mask+" group="+group+" descriptor="+descriptor+" realtime="+realtime+" format="+format+" type="+type+" kind="+kind+" safeKind="+safeKind); 507 508 // apparently need to hack up the descriptor for certain formats 509 switch (format) { 510 case MSG_HRIT_FD: this.descriptor = "FD"; break; 511 case MSG_HRIT_HRV: this.descriptor = "HRV"; break; 512 case LRIT_GOES9: this.descriptor = "GOES9"; break; 513 case LRIT_GOES10: this.descriptor = "GOES10"; break; 514 case LRIT_GOES11: this.descriptor = "GOES11"; break; 515 case LRIT_GOES12: this.descriptor = "GOES12"; break; 516 case LRIT_MET5: this.descriptor = "MET5"; break; 517 case LRIT_MET7: this.descriptor = "MET7"; break; 518 case LRIT_MTSAT1R: this.descriptor = "MTSAT1R"; break; 519 } 520 return new LocalAddeEntry(this); 521 } 522 } 523 524}