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