001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028package edu.wisc.ssec.mcidasv.servermanager; 029 030import java.util.Collections; 031import java.util.List; 032import java.util.Map; 033 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037/** 038 * 039 */ 040public class LocalAddeEntry implements AddeEntry { 041 042 /** Friendly neighborhood logging object. */ 043 static final Logger logger = LoggerFactory.getLogger(LocalAddeEntry.class); 044 045 /** Represents a {@literal "bad"} local ADDE entry. */ 046 // seriously, don't use null unless you REALLY need it. 047 public static final LocalAddeEntry INVALID_ENTRY = new Builder("INVALID", "INVALID", "/dev/null", AddeFormat.INVALID).build(); 048 049 /** Represents a {@literal "bad"} collection of local ADDE entries. */ 050 public static final List<LocalAddeEntry> INVALID_ENTRIES = Collections.singletonList(INVALID_ENTRY); 051 052 /** This prefix is Cygwin abstraction that allows access to arbitrary drives. */ 053 private static final String CYGWIN_PREFIX = "/cygdrive/"; 054 055 /** Length of {@link #CYGWIN_PREFIX}. */ 056 private static final int CYGWIN_PREFIX_LEN = CYGWIN_PREFIX.length(); 057 058 /** Status of this entry. */ 059 private EntryStatus entryStatus = EntryStatus.INVALID; 060 061 // RESOLV.SRV FIELDS 062 /** N1 */ 063 private final String group; 064 065 /** N2 */ 066 // this value is built in a non-obvious way. plz to be dox. 067 private final String descriptor; 068 069 /** RT */ 070 private final boolean realtime; 071 072 /** MCV */ 073 private final AddeFormat format; 074 075 /** R1 */ 076 private final String start; 077 078 /** R2 */ 079 private final String end; 080 081 /** MASK */ 082 private final String fileMask; 083 084 /** C */ 085 private final String name; 086 // END RESOLV.SRV FIELDS 087 088 private String asStringId; 089 090 /** Whether or not this entry is temporary. */ 091 private final boolean isTemporary; 092 093 /** Allows the user to refer to this entry with an arbitrary name. */ 094 private String entryAlias; 095 096 public enum ServerName { 097 // note: if you are add a new server you may need to edit the 098 // AddeFormat enum below, the "formats" field in both 099 // LocalEntryEditor and LocalEntryShortcut, and the _formats dictionary 100 // in mcvadde.py. 101 AREA, AMSE, AMSR, AMRR, GINI, FSDX, OMTP, LV1B, MODS, MODX, MOD4, MOD8, 102 MODR, MSGT, MTST, SMIN, TMIN, MD, INST, WARI, INVALID; 103 } 104 105 /** 106 * The various kinds of local ADDE data understood by McIDAS-V, along with 107 * some helpful metadata. 108 * 109 * <p><ul> 110 * <li>{@literal "Human readable"} format names ({@link #friendlyName}).</li> 111 * <li>Optional tooltip description ({@link #tooltip}).</li> 112 * <li>Type of data ({@link #type}).</li> 113 * <li>File naming pattern {@link #fileFilter}.</li> 114 * </ul></p> 115 * 116 * <p>None of {@code AddeFormat}'s fields should contain {@code null}.</p> 117 */ 118 public enum AddeFormat { 119 // note: if you are adding a new value to this list, you may need to 120 // edit the ServerName enum, the "formats" field in both 121 // LocalEntryEditor and LocalEntryShortcut, and the _formats dictionary 122 // in mcvadde.py. 123 // sorry. :( 124 MCIDAS_AREA(ServerName.AREA, "McIDAS AREA"), 125 MCIDAS_MD(ServerName.MD, "McIDAS MD", "McIDAS MD", EntryType.POINT), 126 AMSRE_L1B(ServerName.AMSR, "AMSR-E L 1b", "AMSR-E Level 1b"), 127 AMSRE_L2A(ServerName.AMSE, "AMSR-E L 2a", "AMSR-E Level 2a"), 128 AMSRE_RAIN_PRODUCT(ServerName.AMRR, "AMSR-E Rain Product"), 129 GINI(ServerName.GINI, "GINI"), 130 131 // TJJ Apr 2015 - temporarily comment out INSAT-3D, since the ADDE 132 // servers had not passed testing and been released prior to the 133 // McIDAS-V 1.5 release 134 135 // INSAT3D_IMAGER(ServerName.INST, "INSAT-3D Imager", "INSAT-3D Imager"), 136 // INSAT3D_SOUNDER(ServerName.INST, "INSAT-3D Sounder", "INSAT-3D Sounder"), 137 138 // TJJ Apr 2015 - temporarily comment out Himawari-8, since the ADDE 139 // servers had not passed testing and been released prior to the 140 // McIDAS-V 1.5 release 141 142 // HIMAWARI8(ServerName.WARI, "Himawari 8", "Himawari 8"), 143 144 LRIT_GOES9(ServerName.FSDX, "LRIT GOES-9", "EUMETCast LRIT GOES-9"), 145 LRIT_GOES10(ServerName.FSDX, "LRIT GOES-10", "EUMETCast LRIT GOES-10"), 146 LRIT_GOES11(ServerName.FSDX, "LRIT GOES-11", "EUMETCast LRIT GOES-11"), 147 LRIT_GOES12(ServerName.FSDX, "LRIT GOES-12", "EUMETCast LRIT GOES-12"), 148 LRIT_MET5(ServerName.FSDX, "LRIT MET-5", "EUMETCast LRIT MET-5"), 149 LRIT_MET7(ServerName.FSDX, "LRIT MET-7", "EUMETCast LRIT MET-7"), 150 LRIT_MTSAT1R(ServerName.FSDX, "LRIT MTSAT-1R", "EUMETCast LRIT MTSAT-1R"), 151 METEOSAT_OPENMTP(ServerName.OMTP, "Meteosat OpenMTP"), 152 METOP_AVHRR_L1B(ServerName.LV1B, "Metop AVHRR L 1b", "Metop AVHRR Level 1b"), 153 MODIS_L1B_MOD02(ServerName.MODS, "MODIS MOD 02 - Level-1B Calibrated Geolocated Radiances", "MODIS Level 1b"), 154 MODIS_L2_MOD06(ServerName.MODX, "MODIS MOD 06 - Cloud Product", "MODIS Level 2 (Cloud Top Properties)"), 155 MODIS_L2_MOD07(ServerName.MODX, "MODIS MOD 07 - Atmospheric Profiles", "MODIS Level 2 (Atmospheric Profile)"), 156 MODIS_L2_MOD35(ServerName.MODX, "MODIS MOD 35 - Cloud Mask", "MODIS Level 2 (Cloud Mask)"), 157 MODIS_L2_MOD04(ServerName.MOD4, "MODIS MOD 04 - Aerosol Product", "MODIS Level 2 (Aerosol)"), 158 MODIS_L2_MOD28(ServerName.MOD8, "MODIS MOD 28 - Sea Surface Temperature", "MODIS Level 2 (Sea Surface Temperature)"), 159 MODIS_L2_MODR(ServerName.MODR, "MODIS MOD R - Corrected Reflectance", "MODIS Level 2 (Corrected Reflectance)"), 160 MSG_HRIT_FD(ServerName.MSGT, "MSG HRIT FD", "MSG HRIT (Full Disk)"), 161 MSG_HRIT_HRV(ServerName.MSGT, "MSG HRIT HRV", "MSG HRIT (High Resolution Visible)"), 162 MTSAT_HRIT(ServerName.MTST, "MTSAT HRIT"), 163 NOAA_AVHRR_L1B(ServerName.LV1B, "NOAA AVHRR L 1b", "NOAA AVHRR Level 1b"), 164 SSMI(ServerName.SMIN, "SSMI", "Terrascan netCDF (SMIN)"), 165 TRMM(ServerName.TMIN, "TRMM", "Terrascan netCDF (TMIN)"), 166 INVALID(ServerName.INVALID, "", "", EntryType.INVALID); 167 168 /** Name of the McIDAS-X server. */ 169 private final ServerName servName; 170 171 /** {@literal "Human readable"} format name. This is returned by {@link #toString()}. */ 172 private final String friendlyName; 173 174 /** Description of the format. */ 175 private final String tooltip; 176 177 /** Data type. Corresponds to {@code TYPE} in {@literal "RESOLV.SRV"}. */ 178 private final EntryType type; 179 180 /** 181 * Filename pattern used when listing files in a directory. 182 * If {@link #servName} is {@link ServerName#MSGT} then 183 * {@literal "*PRO*"} is used, otherwise {@literal "*"}. 184 */ 185 private final String fileFilter; 186 187 /** 188 * Builds an {@literal "ADDE format"} and its associated metadata in 189 * a typesafe way. 190 * 191 * @param servName {@link ServerName} that McIDAS-X uses for this format. 192 * @param friendlyName {@literal "Human readable"} name of the format; returned by {@link #toString()}. 193 * @param tooltip If non-empty, this is used as a tooltip in the local entry editor. 194 * @param type {@link EntryType} used by this format. 195 */ 196 AddeFormat(final ServerName servName, final String friendlyName, final String tooltip, final EntryType type) { 197 this.servName = servName; 198 this.friendlyName = friendlyName; 199 this.tooltip = tooltip; 200 this.type = type; 201 this.fileFilter = (servName != ServerName.MSGT) ? "*" : "*PRO*"; 202 } 203 204 /** 205 * Builds an {@literal "imagery ADDE Format"} <b>without</b> a tooltip. 206 * 207 * @param servName {@link ServerName} that McIDAS-X uses for this format. 208 * @param friendlyName {@literal "Human readable"} name of the format; returned by {@link #toString()}. 209 */ 210 AddeFormat(final ServerName servName, final String friendlyName) { 211 this(servName, friendlyName, "", EntryType.IMAGE); 212 } 213 214 /** 215 * Builds an {@literal "imagery ADDE Format"} <b>with</b> a tooltip. 216 * 217 * @param servName {@link ServerName} that McIDAS-X uses for this format. 218 * @param friendlyName {@literal "Human readable"} name of the format; returned by {@link #toString()}. 219 * @param tooltip If non-empty, this is used as a tooltip in the local entry editor. 220 */ 221 AddeFormat(final ServerName servName, final String friendlyName, final String tooltip) { 222 this(servName, friendlyName, tooltip, EntryType.IMAGE); 223 } 224 225 /** 226 * Gets the McIDAS-X {@link ServerName} for this format. 227 * 228 * @return Either the name of this format's McIDAS-X server, or 229 * {@link ServerName#INVALID}. 230 */ 231 public ServerName getServerName() { 232 return servName; 233 } 234 235 /** 236 * Gets the tooltip text to use in the server manager GUI for this 237 * format. 238 * 239 * @return Text to use as a GUI tooltip. Cannot be {@code null}, though 240 * empty {@code String} values are permitted. 241 */ 242 public String getTooltip() { 243 return tooltip; 244 } 245 246 /** 247 * Gets the type of data used by this format. This value dictates the 248 * chooser(s) where this format can appear. 249 * 250 * @return One of {@link AddeEntry.EntryType EntryType}, or 251 * {@link AddeEntry.EntryType#INVALID INVALID}. 252 */ 253 public EntryType getType() { 254 return type; 255 } 256 257 /** 258 * Gets the string used to filter out files that match this format. 259 * 260 * @return Either a specialized {@code String}, like {@literal "*PRO*"} 261 * or {@literal "*"}. 262 */ 263 public String getFileFilter() { 264 return fileFilter; 265 } 266 267 /** 268 * Gets the {@code String} representation of this format. 269 * 270 * @return the value of {@link #friendlyName}. 271 */ 272 @Override public String toString() { 273 return friendlyName; 274 } 275 } 276 277 /** 278 * 279 * 280 * @param builder 281 * 282 * @see LocalAddeEntry.Builder 283 */ 284 private LocalAddeEntry(final Builder builder) { 285 this.group = builder.group; 286 this.descriptor = builder.descriptor; 287 this.realtime = builder.realtime; 288 this.format = builder.format; 289 this.fileMask = builder.mask; 290 this.name = builder.name; 291 this.start = builder.start; 292 this.end = builder.end; 293 this.entryStatus = builder.status; 294 this.isTemporary = builder.temporary; 295 this.entryAlias = builder.alias; 296 logger.debug("created local: {}", this); 297 } 298 299 @Override public AddeAccount getAccount() { 300 return RemoteAddeEntry.DEFAULT_ACCOUNT; 301 } 302 303 @Override public String getAddress() { 304 return "localhost"; 305 } 306 307 @Override public EntrySource getEntrySource() { 308 return EntrySource.USER; 309 } 310 311 @Override public EntryStatus getEntryStatus() { 312 return entryStatus; 313 } 314 315 @Override public String getEntryText() { 316 return "localhost/"+getGroup(); 317 } 318 319 @Override public EntryType getEntryType() { 320 return format.getType(); 321 } 322 323 @Override public EntryValidity getEntryValidity() { 324 return (isValid()) ? EntryValidity.VERIFIED : EntryValidity.INVALID; 325 } 326 327 // TODO(jon): fix this noop 328 @Override public String getEntryAlias() { 329 String tmp = entryAlias; 330 if (entryAlias == null) { 331 tmp = ""; 332 } 333 return tmp; 334 } 335 336 // TODO(jon): fix this noop 337 @Override public void setEntryAlias(final String newAlias) { 338 if (newAlias == null) { 339 throw new NullPointerException("Null aliases are not allowable."); 340 } 341 this.entryAlias = newAlias; 342 } 343 344 @Override public void setEntryStatus(EntryStatus newStatus) { 345 entryStatus = newStatus; 346 } 347 348 @Override public boolean isEntryTemporary() { 349 return isTemporary; 350 } 351 352 @Override public String getGroup() { 353 return group; 354 } 355 356 @Override public String getName() { 357 return name; 358 } 359 360 /** 361 * Gets the ADDE descriptor for the current local ADDE entry. 362 * 363 * @return ADDE descriptor (corresponds to the {@literal "N2"} section of 364 * a RESOLV.SRV entry). 365 */ 366 public String getDescriptor() { 367 return descriptor; 368 } 369 370 /** 371 * Gets the ADDE dataset format for the current local ADDE entry. 372 * 373 * @return ADDE format (corresponds to the {@literal "MCV"} section of a 374 * RESOLV.SRV entry). 375 */ 376 public AddeFormat getFormat() { 377 return format; 378 } 379 380 /** 381 * Gets the ADDE file mask for the current local ADDE entry. 382 * 383 * @return ADDE file mask (corresponds to the {@literal "MASK"} section 384 * of a RESOLV.SRV entry). 385 */ 386 public String getMask() { 387 return fileMask; 388 } 389 390 /** 391 * Gets the ADDE file mask for the current local ADDE entry. 392 * 393 * @return ADDE file mask (corresponds to the {@literal "MASK"} section 394 * of a RESOLV.SRV entry). 395 */ 396 public String getFileMask() { 397 return fileMask; 398 } 399 400 /** 401 * Gets the ADDE realtime status of the current local ADDE entry. 402 * 403 * @return Whether or not the current dataset is {@literal "realtime"}. 404 * Corresponds to the {@literal "RT"} section of a RESOLV.SRV entry. 405 */ 406 public boolean getRealtime() { 407 return realtime; 408 } 409 410 /** 411 * Gets the starting number of the current local ADDE dataset. 412 * 413 * @return Corresponds to the {@literal "R1"} section of a RESOLV.SRV entry. 414 */ 415 public String getStart() { 416 return start; 417 } 418 419 /** 420 * Gets the ending number of the current local ADDE dataset. 421 * 422 * @return Corresponds to the {@literal "R2"} section of a RESOLV.SRV entry. 423 */ 424 public String getEnd() { 425 return end; 426 } 427 428 /** 429 * Tests the current local ADDE dataset for validity. 430 * 431 * @return {@code true} iff {@link #group} and {@link #name} are not empty. 432 */ 433 public boolean isValid() { 434// return !((group.isEmpty()) || (descriptor.isEmpty()) || (name.isEmpty())); 435 return !(group.isEmpty() || name.isEmpty()); 436 } 437 438 /** 439 * Gets the local ADDE dataset's realtime status as a value suitable for 440 * RESOLV.SRV (one of {@literal "Y"} or {@literal "N"}). 441 * 442 * @return RESOLV.SRV-friendly representation of the current realtime status. 443 */ 444 public String getRealtimeAsString() { 445 return realtime ? "Y" : "N"; 446 } 447 448 /** 449 * @see LocalAddeEntry#generateHashCode(String, String, String, String, boolean, AddeFormat) 450 */ 451 @Override public int hashCode() { 452 return generateHashCode(name, group, fileMask, entryAlias, isTemporary, format); 453 } 454 455 /** 456 * Checks a given object for equality with the current {@code LocalAddeEntry} 457 * instance. 458 * 459 * @param obj Object to check. {@code null} values allowed. 460 * 461 * @return {@code true} if {@code obj} is {@literal "equal"} to the current 462 * {@code LocalAddeEntry} instance. 463 */ 464 @Override public boolean equals(Object obj) { 465 if (this == obj) { 466 return true; 467 } 468 if (obj == null) { 469 return false; 470 } 471 if (!(obj instanceof LocalAddeEntry)) { 472 return false; 473 } 474 LocalAddeEntry other = (LocalAddeEntry) obj; 475 if (fileMask == null) { 476 if (other.fileMask != null) { 477 return false; 478 } 479 } else if (!fileMask.equals(other.fileMask)) { 480 return false; 481 } 482 if (format == null) { 483 if (other.format != null) { 484 return false; 485 } 486 } else if (!format.toString().equals(other.format.toString())) { 487 return false; 488 } 489 if (group == null) { 490 if (other.group != null) { 491 return false; 492 } 493 } else if (!group.equals(other.group)) { 494 return false; 495 } 496 if (name == null) { 497 if (other.name != null) { 498 return false; 499 } 500 } else if (!name.equals(other.name)) { 501 return false; 502 } 503 if (entryAlias == null) { 504 if (other.entryAlias != null) { 505 return false; 506 } 507 } else if (!entryAlias.equals(other.entryAlias)) { 508 return false; 509 } 510 if (isTemporary != other.isTemporary) { 511 return false; 512 } 513 return true; 514 } 515 516 @Override public String asStringId() { 517 if (asStringId == null) { 518 asStringId = "localhost!"+group+'!'+EntryType.IMAGE.name()+'!'+name; 519 } 520 return asStringId; 521 } 522 523 @Override public String toString() { 524 return String.format( 525 "[LocalAddeEntry@%x: name=%s, group=%s, fileMask=\"%s\", descriptor=%s, serverName=%s, format=%s, description=%s, type=%s, status=%s, temporary=%s, alias=%s]", 526 hashCode(), name, group, fileMask, descriptor, format.getServerName().name(), format.name(), format.getTooltip(), format.getType(), entryStatus.name(), isTemporary, entryAlias); 527 528 } 529 530 public static int generateHashCode(final LocalAddeEntry entry) { 531 return generateHashCode(entry.getName(), entry.getGroup(), entry.getMask(), entry.getEntryAlias(), entry.isEntryTemporary(), entry.getFormat()); 532 } 533 534 public static int generateHashCode(String name, String group, String fileMask, String entryAlias, boolean isTemporary, AddeFormat format) { 535 final int prime = 31; 536 int result = 1; 537 result = prime * result 538 + ((fileMask == null) ? 0 : fileMask.hashCode()); 539 result = prime * result + ((format == null) ? 0 : format.toString().hashCode()); 540 result = prime * result + ((group == null) ? 0 : group.hashCode()); 541 result = prime * result + ((name == null) ? 0 : name.hashCode()); 542 result = prime * result + ((entryAlias == null) ? 0 : entryAlias.hashCode()); 543 result = prime * result + (isTemporary ? 1231 : 1237); 544 return result; 545 } 546 547 /** 548 * A builder of (mostly) immutable {@link LocalAddeEntry} instances. 549 * 550 * <p>Usage example: <pre> {@code 551 * LocalAddeEntry entry = new LocalAddeEntry 552 * .Builder(group, name, format, mask) 553 * .realtime("Y") 554 * .range(start, end) 555 * .type(EntryType.POINT) 556 * .build();}</pre> 557 * 558 * Only the values required by the Builder constructor are required. 559 */ 560 public static class Builder { 561 // required 562 /** Corresponds to RESOLV.SRV's {@literal "N1"} section. */ 563 private final String group; 564 565 /** Corresponds to RESOLV.SRV's {@literal "C"} section. */ 566 private final String name; 567 568 /** Corresponds to RESOLV.SRV's {@literal "MCV"} section. */ 569 private final AddeFormat format; 570 571 /** Corresponds to RESOLV.SRV's {@literal "MASK"} section. */ 572 private final String mask; 573 574 // generated 575 private String descriptor; 576 577 // optional 578 /** 579 * Corresponds to RESOLV.SRV's {@literal "RT"} section. 580 * Defaults to {@code false}. 581 */ 582 private boolean realtime = false; 583 584 /** 585 * Corresponds to RESOLV.SRV's {@literal "R1"} section. 586 * Defaults to {@literal "1"}. 587 */ 588 private String start = "1"; 589 590 /** 591 * Corresponds to RESOLV.SRV's {@literal "R2"} section. 592 * Defaults to {@literal "999999"}. 593 */ 594 private String end = "999999"; 595 596 /** 597 * Defaults to {@link AddeEntry.EntryStatus#INVALID}. 598 */ 599 private EntryStatus status = EntryStatus.INVALID; 600 601 /** 602 * Corresponds to RESOLV.SRV's {@literal "TYPE"} section. 603 * Defaults to {@link AddeEntry.EntryType#IMAGE IMAGE}. 604 */ 605 private EntryType type = EntryType.IMAGE; 606 607 /** 608 * Corresponds to RESOLV.SRV's {@literal "K"} section. 609 * Defaults to {@literal "NOT_SET"}. 610 */ 611 private String kind = "NOT_SET"; 612 613 /** 614 * Defaults to {@link ServerName#INVALID}. 615 */ 616 private ServerName safeKind = ServerName.INVALID; 617 618 /** */ 619 private boolean temporary = false; 620 621 /** */ 622 private String alias = ""; 623 624 public Builder(final Map<String, String> map) { 625 if (!map.containsKey("C") || !map.containsKey("N1") || !map.containsKey("MASK") || !map.containsKey("MCV")) { 626 throw new IllegalArgumentException("Cannot build a LocalAddeEntry without the following keys: C, N1, MASK, and MCV."); 627 } 628 629 this.name = map.get("C"); 630 this.group = map.get("N1"); 631 this.mask = map.get("MASK"); 632 this.format = EntryTransforms.strToAddeFormat(map.get("MCV")); 633 634// descriptor(map.get("N2")); 635 type(EntryTransforms.strToEntryType(map.get("TYPE"))); 636 kind(map.get("K").toUpperCase()); 637 realtime(map.get("RT")); 638 start(map.get("R1")); 639 end(map.get("R2")); 640 641 if (map.containsKey("TEMPORARY")) { 642 temporary(map.get("TEMPORARY")); 643 } 644 } 645 646 /** 647 * Creates a new {@code LocalAddeEntry} {@literal "builder"} with the 648 * required fields for a {@code LocalAddeEntry} object. 649 * 650 * @param name 651 * @param group 652 * @param mask 653 * @param format 654 */ 655 public Builder(final String name, final String group, final String mask, final AddeFormat format) { 656 this.name = name; 657 this.group = group; 658 this.mask = mask; 659 this.format = format; 660 } 661 662 /** 663 * This method is currently a no-op. 664 * 665 * @param descriptor 666 * 667 * @return {@code LocalAddeEntry.Builder} with ADDE descriptor. 668 */ 669 public Builder descriptor(final String descriptor) { 670// if (descriptor != null) { 671// this.descriptor = descriptor; 672// } 673 return this; 674 } 675 676 /** 677 * 678 * 679 * @param realtimeAsStr 680 * 681 * @return {@code LocalAddeEntry.Builder} with ADDE realtime flag. 682 */ 683 // looks like mcidasx understands ("Y"/"N"/"A") 684 // should probably ignore case and accept "YES"/"NO"/"ARCHIVE" 685 // in addition to the normal boolean conversion from String 686 public Builder realtime(final String realtimeAsStr) { 687 if (realtimeAsStr == null) { 688 return this; 689 } 690 691 if ("Y".equalsIgnoreCase(realtimeAsStr) || "YES".equalsIgnoreCase(realtimeAsStr)) { 692 this.realtime = true; 693 } else { 694 this.realtime = Boolean.valueOf(realtimeAsStr); 695 } 696 return this; 697 } 698 699 /** 700 * 701 * 702 * @param realtime 703 * 704 * @return {@code LocalAddeEntry.Builder} with ADDE realtime flag. 705 */ 706 public Builder realtime(final boolean realtime) { 707 this.realtime = realtime; 708 return this; 709 } 710 711 /** 712 * 713 * 714 * @param type 715 * 716 * @return {@code LocalAddeEntry.Builder} with ADDE data type. 717 */ 718 // my assumption is that if "format" is known, you can infer "type" 719 public Builder type(final EntryType type) { 720 if (type != null) { 721 this.type = type; 722 } 723 return this; 724 } 725 726 /** 727 * 728 * 729 * @param kind 730 * 731 * @return {@code LocalAddeEntry.Builder} with ADDE kind. 732 */ 733 // my assumption is that if "format" is known, you can infer "kind" 734 public Builder kind(final String kind) { 735 if (kind == null) { 736 return this; 737 } 738 739 this.kind = kind; 740 try { 741 this.safeKind = ServerName.valueOf(kind); 742 } catch (IllegalArgumentException e) { 743 this.safeKind = ServerName.INVALID; 744 } 745 return this; 746 } 747 748 /** 749 * 750 * 751 * @param start 752 * 753 * @return {@code LocalAddeEntry.Builder} with ADDE dataset {@literal "start"}. 754 */ 755 public Builder start(final String start) { 756 if (start != null) { 757 this.start = start; 758 } 759 return this; 760 } 761 762 /** 763 * 764 * 765 * @param end 766 * 767 * @return {@code LocalAddeEntry.Builder} with ADDE dataset {@literal "end"}. 768 */ 769 public Builder end(final String end) { 770 if (end != null) { 771 this.end = end; 772 } 773 return this; 774 } 775 776 /** 777 * 778 * 779 * @param start 780 * @param end 781 * 782 * @return {@code LocalAddeEntry.Builder} with ADDE dataset {@literal "start" and "end"} values. 783 */ 784 public Builder range(final String start, final String end) { 785 if (start != null && end != null) { 786 this.start = start; 787 this.end = end; 788 } 789 return this; 790 } 791 792 /** 793 * 794 * 795 * @param status 796 * 797 * @return {@code LocalAddeEntry.Builder} with {@link AddeEntry.EntryStatus}. 798 */ 799 public Builder status(final String status) { 800 if (status != null && status.length() > 0) { 801 this.status = EntryTransforms.strToEntryStatus(status); 802 } 803 return this; 804 } 805 806 /** 807 * 808 * 809 * @param status 810 * 811 * @return {@code LocalAddeEntry.Builder} with {@link AddeEntry.EntryStatus}. 812 */ 813 public Builder status(final EntryStatus status) { 814 if (status != null) { 815 this.status = status; 816 } 817 return this; 818 } 819 820 /** 821 * 822 * 823 * @param temporary 824 * 825 * @return {@code LocalAddeEntry.Builder} with the specified temporary status. 826 */ 827 public Builder temporary(final boolean temporary) { 828 this.temporary = temporary; 829 return this; 830 } 831 832 public Builder temporary(final String temporary) { 833 this.temporary = Boolean.valueOf(temporary); 834 return this; 835 } 836 837 /** 838 * 839 * 840 * @param alias 841 * 842 * @return {@code LocalAddeEntry.Builder} with the specified alias. 843 */ 844 public Builder alias(final String alias) { 845 this.alias = alias; 846 return this; 847 } 848 849 /** 850 * 851 * 852 * @return New {@code LocalAddeEntry} instance. 853 */ 854 public LocalAddeEntry build() { 855 // apparently need to hack up the descriptor for certain formats 856 switch (format) { 857 case MSG_HRIT_FD: this.descriptor = "FD"; break; 858 case MSG_HRIT_HRV: this.descriptor = "HRV"; break; 859 case LRIT_GOES9: this.descriptor = "GOES9"; break; 860 case LRIT_GOES10: this.descriptor = "GOES10"; break; 861 case LRIT_GOES11: this.descriptor = "GOES11"; break; 862 case LRIT_GOES12: this.descriptor = "GOES12"; break; 863 case LRIT_MET5: this.descriptor = "MET5"; break; 864 case LRIT_MET7: this.descriptor = "MET7"; break; 865 case LRIT_MTSAT1R: this.descriptor = "MTSAT1R"; break; 866 default: 867 this.descriptor = Integer.toHexString(generateHashCode(name, group, mask, alias, temporary, format)); 868 break; 869 } 870 return new LocalAddeEntry(this); 871 } 872 } 873}