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