13.5.1-15 baseline
Former-commit-id:53e96e21db
[formerly53e96e21db
[formerly dbf9107f9c775f9a1df759bc341c0722f5fc92f2]] Former-commit-id:c6dc1817e7
Former-commit-id:65beefde58
This commit is contained in:
parent
0f3ffc3892
commit
9ea3f0c215
13 changed files with 1034 additions and 189 deletions
|
@ -20,6 +20,8 @@
|
|||
package com.raytheon.viz.gfe.vtec;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
@ -44,6 +46,9 @@ import com.raytheon.viz.texteditor.util.VtecUtil;
|
|||
* Jul 19, 2013 #1842 dgilling Use VtecUtil.replaceFirstVtecString()
|
||||
* to ensure start times of in progress
|
||||
* events aren't set to the wrong time.
|
||||
* Aug 07, 2013 #1842 dgilling Fix ETN assignment for products with
|
||||
* multiple NEW segments with the same
|
||||
* phensig.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -82,6 +87,13 @@ public class GFEVtecUtil {
|
|||
return message;
|
||||
}
|
||||
|
||||
// With GFE VTEC products, it's possible to have multiple segments with
|
||||
// NEW vtec action codes and the same phensig. For this reason,
|
||||
// HazardsTable.py implemented a "cache" that would ensure all NEWs for
|
||||
// the same phensig would be assigned the same ETN. This Map replicates
|
||||
// that legacy behavior.
|
||||
Map<String, Integer> etnCache = new HashMap<String, Integer>();
|
||||
|
||||
Matcher vtecMatcher = VtecUtil.VTEC_REGEX.matcher(message);
|
||||
StringBuffer finalOutput = new StringBuffer();
|
||||
while (vtecMatcher.find()) {
|
||||
|
@ -93,8 +105,13 @@ public class GFEVtecUtil {
|
|||
&& ((!NATIONAL_PHENSIGS.contains(vtec.getPhensig())) || (IGNORE_NATIONAL_ETN
|
||||
.contains(vtec.getOffice()) && TROPICAL_PHENSIGS
|
||||
.contains(vtec.getPhensig())))) {
|
||||
int newEtn = VtecUtil.getNextEtn(vtec.getOffice(),
|
||||
String cacheKey = vtec.getPhensig();
|
||||
Integer newEtn = etnCache.get(cacheKey);
|
||||
if (newEtn == null) {
|
||||
newEtn = VtecUtil.getNextEtn(vtec.getOffice(),
|
||||
vtec.getPhensig(), true);
|
||||
etnCache.put(cacheKey, newEtn);
|
||||
}
|
||||
vtec.setSequence(newEtn);
|
||||
}
|
||||
vtecMatcher
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
|
|||
* ------------ ---------- ----------- --------------------------
|
||||
* Initial creation
|
||||
* May 7, 2013 1973 rferrel Changes to properly display Issue Time.
|
||||
* Aug 7, 2013 2243 jsanchez Set all the attributes of an AbstractWarningRecord and added an expiration string.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -50,22 +51,25 @@ public class FollowupData extends WarningRecord {
|
|||
/**
|
||||
* String displayed in the drop down update list.
|
||||
*/
|
||||
public String displayString;
|
||||
private String displayString;
|
||||
|
||||
/**
|
||||
* String used to test if this object is equivalent to one of the updated
|
||||
* items in the drop down.
|
||||
*/
|
||||
public String equvialentString;
|
||||
private String equvialentString;
|
||||
|
||||
/**
|
||||
* Information string used when the follow up is no longer valid or allowed.
|
||||
*/
|
||||
private String expirationString;
|
||||
|
||||
public FollowupData(WarningAction action, AbstractWarningRecord record) {
|
||||
super((WarningRecord) record);
|
||||
setAct(action.toString());
|
||||
setOfficeid(record.getOfficeid());
|
||||
setPhen(record.getPhen());
|
||||
setSig(record.getSig());
|
||||
setEtn(record.getEtn());
|
||||
|
||||
displayString = getDisplayString(action, record);
|
||||
displayString = createDisplayString(action, record);
|
||||
expirationString = createExpirationString(action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +80,7 @@ public class FollowupData extends WarningRecord {
|
|||
* @param record
|
||||
* @return
|
||||
*/
|
||||
private String getDisplayString(WarningAction status,
|
||||
private String createDisplayString(WarningAction status,
|
||||
AbstractWarningRecord record) {
|
||||
StringBuilder rval = new StringBuilder();
|
||||
if (record.getProductClass().equals("T")) {
|
||||
|
@ -98,6 +102,29 @@ public class FollowupData extends WarningRecord {
|
|||
return rval.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the expiration string based on the action. The expiration string
|
||||
* provides an explanation of why the follow up data is no longer valid.
|
||||
*
|
||||
* @param action
|
||||
* @return
|
||||
*/
|
||||
private String createExpirationString(WarningAction action) {
|
||||
String message = null;
|
||||
if (action == WarningAction.NEW) {
|
||||
message = "Reissue no longer allowed; after 30 minutes of warning expiration.";
|
||||
} else if (action == WarningAction.COR) {
|
||||
message = "Correction no longer allowed; after 10 minutes of warning issuance.";
|
||||
} else if (action == WarningAction.CAN) {
|
||||
message = "Cancellation no longer allowed; within 10 minutes of warning expiration.";
|
||||
} else if (action == WarningAction.CON) {
|
||||
message = "Continuation no longer allowed; within 5 minutes of warning expiration.";
|
||||
} else if (action == WarningAction.EXP) {
|
||||
message = "Expiration no longer allowed; after 10 minutes of warning expiration.";
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string informing the user when a product was issued or when it
|
||||
* will expire. This is appended to the product in the "Update List"
|
||||
|
@ -143,4 +170,16 @@ public class FollowupData extends WarningRecord {
|
|||
&& this.getEtn().equals(obj.getEtn());
|
||||
}
|
||||
|
||||
public String getDisplayString() {
|
||||
return displayString;
|
||||
}
|
||||
|
||||
public String getEquvialentString() {
|
||||
return equvialentString;
|
||||
}
|
||||
|
||||
public String getExpirationString() {
|
||||
return expirationString;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ import com.raytheon.uf.common.status.IUFStatusHandler;
|
|||
import com.raytheon.uf.common.status.UFStatus;
|
||||
import com.raytheon.uf.common.status.UFStatus.Priority;
|
||||
import com.raytheon.uf.common.time.SimulatedTime;
|
||||
import com.raytheon.uf.common.time.TimeRange;
|
||||
import com.raytheon.uf.viz.core.IDisplayPaneContainer;
|
||||
import com.raytheon.uf.viz.core.VizApp;
|
||||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
|
@ -142,6 +141,7 @@ import com.vividsolutions.jts.geom.Polygon;
|
|||
* Jun 24, 2013 DR 16317 D. Friedman Handle "motionless" track.
|
||||
* Jul 16, 2013 DR 16387 Qinglu Lin Reset totalSegments for each followup product.
|
||||
* Jul 29, 2013 DR 16352 D. Friedman Move 'result' to okPressed().
|
||||
* Aug 6, 2013 2243 jsanchez Refreshed the follow up list every minute.
|
||||
* </pre>
|
||||
*
|
||||
* @author chammack
|
||||
|
@ -274,9 +274,9 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
|
||||
private Group productType;
|
||||
|
||||
private IWarngenObserver wed = new WarningSender();
|
||||
private boolean invalidFollowUpAction = false;
|
||||
|
||||
private TimeRange timeRange = null;
|
||||
private IWarngenObserver wed = new WarningSender();
|
||||
|
||||
public WarngenDialog(Shell parentShell, WarngenLayer layer) {
|
||||
super(parentShell, SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE,
|
||||
|
@ -823,6 +823,9 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
|| warngenLayer.getPolygon().isEmpty()) {
|
||||
str += WarngenConstants.INSTRUCTION_NO_SHADED_AREA;
|
||||
createTextButtonEnabled = false;
|
||||
} else if (invalidFollowUpAction) {
|
||||
str += "Select a different follow up item";
|
||||
createTextButtonEnabled = false;
|
||||
}
|
||||
if (okButton != null) {
|
||||
okButton.setEnabled(createTextButtonEnabled);
|
||||
|
@ -831,8 +834,8 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
if (warngenLayer.getWarningArea() == null) {
|
||||
str = "Area selected has no overlap with current area of responsibility";
|
||||
} else {
|
||||
if (warngenLayer.getStormTrackState().isInitiallyMotionless() &&
|
||||
! warngenLayer.getStormTrackState().isNonstationary()) {
|
||||
if (warngenLayer.getStormTrackState().isInitiallyMotionless()
|
||||
&& !warngenLayer.getStormTrackState().isNonstationary()) {
|
||||
str += WarngenConstants.INSTRUCTION_DRAG_STORM + "\n";
|
||||
} else if (warngenLayer.getStormTrackState().trackVisible) {
|
||||
str += "Adjust Centroid in any Frame" + "\n";
|
||||
|
@ -880,7 +883,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
if (FollowUpUtil.checkApplicable(site,
|
||||
warngenLayer.getConfiguration(), warnings.get(i), act)) {
|
||||
FollowupData data = new FollowupData(act, warnings.get(i));
|
||||
updateListCbo.setData(data.displayString, data);
|
||||
updateListCbo.setData(data.getDisplayString(), data);
|
||||
if (act == WarningAction.NEW) {
|
||||
newYes = true;
|
||||
} else if (act == WarningAction.EXT) {
|
||||
|
@ -892,7 +895,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
} else if (act == WarningAction.COR) {
|
||||
corYes = true;
|
||||
}
|
||||
dropDownItems.add(data.displayString);
|
||||
dropDownItems.add(data.getDisplayString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -930,19 +933,54 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
updateListCbo.add(dropDownItems.get(i));
|
||||
}
|
||||
// Select the previously selected item.
|
||||
invalidFollowUpAction = false;
|
||||
if (currentSelection != null) {
|
||||
boolean isValid = false;
|
||||
for (int i = 0; i < updateListCbo.getItemCount(); i++) {
|
||||
if (updateListCbo.getItem(i).startsWith(
|
||||
currentSelection.equvialentString)) {
|
||||
currentSelection.getEquvialentString())) {
|
||||
updateListCbo.select(i);
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
invalidFollowUpAction = true;
|
||||
preventFollowUpAction(currentSelection);
|
||||
}
|
||||
} else {
|
||||
updateListCbo.select(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the user from creating text when the current selection is no
|
||||
* longer valid or available in the update list. The polygon will also be
|
||||
* locked from being modified and return to it's last issued state if the
|
||||
* polygon was temporarily modified.
|
||||
*
|
||||
* @param currentSelection
|
||||
*/
|
||||
private void preventFollowUpAction(FollowupData currentSelection) {
|
||||
|
||||
try {
|
||||
warngenLayer.createPolygonFromRecord(currentSelection);
|
||||
} catch (VizException e) {
|
||||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error resetting the polygon\n", e);
|
||||
}
|
||||
|
||||
bulletList.setEnabled(false);
|
||||
setPolygonLocked(true);
|
||||
setTrackLocked(true);
|
||||
refreshDisplay();
|
||||
|
||||
// Provide an info message when this situation occurs
|
||||
statusHandler.handle(Priority.PROBLEM,
|
||||
currentSelection.getExpirationString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the possible durations
|
||||
*
|
||||
|
@ -995,8 +1033,8 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
return;
|
||||
}
|
||||
|
||||
if (followupData != null && WarningAction.valueOf(followupData
|
||||
.getAct()) == WarningAction.NEW) {
|
||||
if (followupData != null
|
||||
&& WarningAction.valueOf(followupData.getAct()) == WarningAction.NEW) {
|
||||
redrawFromWarned();
|
||||
}
|
||||
|
||||
|
@ -1021,9 +1059,10 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
try {
|
||||
monitor.beginTask("Generating product", 1);
|
||||
long t0 = System.currentTimeMillis();
|
||||
String result = TemplateRunner.runTemplate(warngenLayer,
|
||||
startTime.getTime(), endTime.getTime(),
|
||||
selectedBullets, followupData, backupData);
|
||||
String result = TemplateRunner.runTemplate(
|
||||
warngenLayer, startTime.getTime(),
|
||||
endTime.getTime(), selectedBullets,
|
||||
followupData, backupData);
|
||||
resultContainer[0] = result;
|
||||
Matcher m = FollowUpUtil.vtecPtrn.matcher(result);
|
||||
totalSegments = 0;
|
||||
|
@ -1123,15 +1162,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
// again after a product was issued.AWIPS I does not auto update their
|
||||
// update list, this is their solution.
|
||||
if (followupData != null && totalSegments > 1) {
|
||||
multiSegmentMessage(followupData.equvialentString);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timeRange != null
|
||||
&& timeRange.contains(SimulatedTime.getSystemTime().getTime()) == false) {
|
||||
// The action is no longer available in the follow up/update list
|
||||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Follow up product has nothing to follow up.");
|
||||
multiSegmentMessage(followupData.getEquvialentString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1188,7 +1219,6 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
bulletList.setEnabled(true);
|
||||
recreateUpdates();
|
||||
damBreakInstruct = null;
|
||||
timeRange = null;
|
||||
extEndTime = null;
|
||||
totalSegments = 0;
|
||||
bulletListManager.recreateBullets(warngenLayer.getConfiguration()
|
||||
|
@ -1425,7 +1455,6 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
warngenLayer.state.followupData = null;
|
||||
warngenLayer.getStormTrackState().endTime = null;
|
||||
damBreakInstruct = null;
|
||||
timeRange = null;
|
||||
extEndTime = null;
|
||||
totalSegments = 0;
|
||||
|
||||
|
@ -1448,6 +1477,8 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
changeBtn.setEnabled(!enableDuration);
|
||||
recreateDurations(durationList);
|
||||
|
||||
// Current selection doesn't matter anymore
|
||||
updateListCbo.select(0);
|
||||
// update list
|
||||
recreateUpdates();
|
||||
|
||||
|
@ -1571,7 +1602,8 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
.valueOf(warngenLayer.state.followupData
|
||||
.getAct()) == WarningAction.CON
|
||||
&& totalSegments > 1) {
|
||||
sameProductMessage(warngenLayer.state.followupData.equvialentString);
|
||||
sameProductMessage(warngenLayer.state.followupData
|
||||
.getEquvialentString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1581,7 +1613,8 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
// Sets the updatelist with the last selected vtec option
|
||||
for (int i = 0; i < updateListCbo.getItemCount(); i++) {
|
||||
String item = updateListCbo.getItem(i);
|
||||
if (item.equals(warngenLayer.state.followupData.displayString)) {
|
||||
if (item.equals(warngenLayer.state.followupData
|
||||
.getDisplayString())) {
|
||||
updateListCbo.select(i);
|
||||
updateListCbo.setText(item);
|
||||
data = warngenLayer.state.followupData;
|
||||
|
@ -1823,6 +1856,32 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
}
|
||||
|
||||
timer.schedule(updateTimeTask, delay, delay);
|
||||
|
||||
TimerTask recreateUpdatesTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
getDisplay().syncExec(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
recreateUpdates();
|
||||
} catch (Exception e) {
|
||||
statusHandler.handle(Priority.PROBLEM,
|
||||
"WarnGen Error", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update the follow up list every minute
|
||||
long currentTimeInSeconds = SimulatedTime.getSystemTime().getMillis() / 1000;
|
||||
long secondsToNextMinute = 0;
|
||||
if (currentTimeInSeconds % 60 != 0) {
|
||||
secondsToNextMinute = 60 - (currentTimeInSeconds % 60);
|
||||
}
|
||||
timer.schedule(recreateUpdatesTask, secondsToNextMinute * 1000,
|
||||
60 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1897,7 +1956,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
timeRange = FollowUpUtil.getTimeRange(WarningAction.CON, newWarn);
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
@ -1943,7 +2002,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
}
|
||||
timeRange = FollowUpUtil.getTimeRange(WarningAction.COR, newWarn);
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
@ -1981,7 +2040,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
timeRange = FollowUpUtil.getTimeRange(WarningAction.EXP, newWarn);
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
@ -2019,7 +2078,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
timeRange = FollowUpUtil.getTimeRange(WarningAction.CAN, newWarn);
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
@ -2044,7 +2103,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
timeRange = null;
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
@ -2087,7 +2146,7 @@ public class WarngenDialog extends CaveSWTDialog implements
|
|||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error creating polygon from the record\n", e);
|
||||
}
|
||||
timeRange = FollowUpUtil.getTimeRange(WarningAction.EXT, newWarn);
|
||||
|
||||
return newWarn;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.raytheon.viz.warngen.text.ICommonPatterns;
|
|||
* Jul 22, 2008 #1284 bwoodle Initial creation
|
||||
* Oct 18, 2012 15332 jsanchez Fixed refactor bugs.
|
||||
* Mar 13, 2013 DR 15892 D. Friedman Handle SMW format in canceledAreasFromText
|
||||
* Aug 6, 2013 2243 jsanchez Updated the time ranges to be removed from the follow up list correctly.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -202,7 +203,8 @@ public class FollowUpUtil {
|
|||
headline += line;
|
||||
}
|
||||
}
|
||||
String[] ugcs = FipsUtil.getListCounties(ugcLine).toArray(new String[0]);
|
||||
String[] ugcs = FipsUtil.getListCounties(ugcLine)
|
||||
.toArray(new String[0]);
|
||||
String[] names;
|
||||
boolean smwAreas = false;
|
||||
if (namesLine.length() > 0)
|
||||
|
@ -242,7 +244,8 @@ public class FollowUpUtil {
|
|||
if (i < names.length) {
|
||||
if (!smwAreas && names[i].length() >= 3) {
|
||||
name = names[i].substring(0, names[i].length() - 3);
|
||||
stateAbbreviation = names[i].substring(names[i].length() - 2);
|
||||
stateAbbreviation = names[i]
|
||||
.substring(names[i].length() - 2);
|
||||
} else {
|
||||
name = names[i];
|
||||
}
|
||||
|
@ -314,48 +317,54 @@ public class FollowUpUtil {
|
|||
|
||||
TimeRange rval = null;
|
||||
|
||||
// The time ranges are offset by 1 minute so that after a refresh and on
|
||||
// the final minute of the time range the follow up data will be
|
||||
// removed. For example, if a CON is only a available until 5 minutes
|
||||
// before a warnings expiration, when the time reaches 5 minutes the
|
||||
// follow up data for a CON is correctly removed.
|
||||
if (action == WarningAction.NEW) {
|
||||
/* Calculate NEW Time Range */
|
||||
start.setTime(record.getEndTime().getTime());
|
||||
start.add(Calendar.MINUTE, -20);
|
||||
start.add(Calendar.MINUTE, -21);
|
||||
end.setTime(record.getEndTime().getTime());
|
||||
end.add(Calendar.MINUTE, 30);
|
||||
end.add(Calendar.MINUTE, 29);
|
||||
rval = new TimeRange(start, end);
|
||||
} else if (action == WarningAction.COR) {
|
||||
/* Calculate COR Time Range */
|
||||
end.setTime(record.getIssueTime().getTime());
|
||||
end.add(Calendar.MINUTE, 10);
|
||||
end.add(Calendar.MINUTE, 9);
|
||||
rval = new TimeRange(record.getStartTime(), end);
|
||||
} else if (action == WarningAction.CAN) {
|
||||
/* Calculate CAN Time Range */
|
||||
end.setTime(record.getEndTime().getTime());
|
||||
end.add(Calendar.MINUTE, -10);
|
||||
end.add(Calendar.MINUTE, -11);
|
||||
rval = new TimeRange(record.getStartTime(), end);
|
||||
} else if (action == WarningAction.CON) {
|
||||
/* Calculate CON Time Range */
|
||||
end.setTime(record.getEndTime().getTime());
|
||||
end.add(Calendar.MINUTE, -5);
|
||||
end.add(Calendar.MINUTE, -6);
|
||||
rval = new TimeRange(record.getStartTime(), end);
|
||||
} else if (action == WarningAction.EXP) {
|
||||
/* Calculate EXP Time Range */
|
||||
start.setTime(record.getEndTime().getTime());
|
||||
start.add(Calendar.MINUTE, -10);
|
||||
start.add(Calendar.MINUTE, -11);
|
||||
end.setTime(record.getEndTime().getTime());
|
||||
end.add(Calendar.MINUTE, 10);
|
||||
end.add(Calendar.MINUTE, 9);
|
||||
rval = new TimeRange(start, end);
|
||||
} else if (action == WarningAction.EXT) {
|
||||
/* Calculate EXT Time Range */
|
||||
start.setTime(record.getStartTime().getTime());
|
||||
end.setTime(record.getEndTime().getTime());
|
||||
end.add(Calendar.MINUTE, -5);
|
||||
end.add(Calendar.MINUTE, -6);
|
||||
rval = new TimeRange(start, end);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** Parses the canceled areas of an SMW, which have a different format
|
||||
* from other products.
|
||||
/**
|
||||
* Parses the canceled areas of an SMW, which have a different format from
|
||||
* other products.
|
||||
*/
|
||||
private static String[] parseSMWCanceledAreas(String[] splitLines) {
|
||||
StringBuilder text = new StringBuilder(64);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<parameter name="staElev" queryName="location.elevation" type="FLOAT" unit="m" />
|
||||
<parameter name="rptType" queryName="reportType" type="INT" />
|
||||
<parameter name="dataURI" queryName="dataURI" type="STRING" />
|
||||
<parameter name="stationId" queryName="location.stationId" type="STRING" />
|
||||
<parameter name="validTime" queryName="validTime" numDims="1" type="LONG" unit="ms" />
|
||||
<parameter name="relTime" queryName="validTime" numDims="1" type="LONG" unit="ms" />
|
||||
<parameter name="refTime" queryName="dataTime.refTime" numDims="1" type="LONG" unit="ms" />
|
||||
|
|
|
@ -93,6 +93,8 @@ import com.raytheon.uf.edex.database.query.DatabaseQuery;
|
|||
* 05/22/13 #2025 dgilling Re-implement functions needed by
|
||||
* GetLatestDbTimeRequest and GetLatestModelDbIdRequest.
|
||||
* 05/20/13 #2127 rjpeter Set session's to read only and switched to stateless where possible.
|
||||
* 08/08/13 DR16485 ryu Remove call to getDatabaseId() from getMaxInsertTimeByDbId()
|
||||
* so new GFE databases aren't accidentally created.
|
||||
* </pre>
|
||||
*
|
||||
* @author bphillip
|
||||
|
@ -1126,7 +1128,13 @@ public class GFEDao extends DefaultPluginDao {
|
|||
public Date getMaxInsertTimeByDbId(final DatabaseID dbId)
|
||||
throws DataAccessLayerException {
|
||||
DatabaseQuery query = new DatabaseQuery(this.daoClass);
|
||||
query.addQueryParam("parmId.dbId", getDatabaseId(dbId),
|
||||
query.addQueryParam("parmId.dbId.siteId", dbId.getSiteId(),
|
||||
QueryOperand.EQUALS);
|
||||
query.addQueryParam("parmId.dbId.format", dbId.getFormat(),
|
||||
QueryOperand.EQUALS);
|
||||
query.addQueryParam("parmId.dbId.modelName", dbId.getModelName(),
|
||||
QueryOperand.EQUALS);
|
||||
query.addQueryParam("parmId.dbId.modelTime", dbId.getModelTime(),
|
||||
QueryOperand.EQUALS);
|
||||
query.addReturnedField("insertTime");
|
||||
query.addOrder("insertTime", false);
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.TreeMap;
|
|||
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import com.raytheon.uf.common.dataplugin.ffmp.collections.ArrayBackedMap;
|
||||
import com.raytheon.uf.common.dataplugin.ffmp.collections.BasinMapFactory;
|
||||
import com.raytheon.uf.common.serialization.ISerializableObject;
|
||||
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
|
||||
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
|
||||
|
@ -48,6 +50,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
|
|||
* TreeMap creation to the tertiary loader.
|
||||
* Apr 26, 2013 1954 bsteffen Minor code cleanup throughout FFMP.
|
||||
* Jul 15, 2013 2184 dhladky Remove all HUC's for storage except ALL
|
||||
* Jul 31, 2013 2242 bsteffen Optimize FFMP NavigableMap memory.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -71,6 +74,13 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
@Transient
|
||||
protected NavigableMap<Date, Float> values;
|
||||
|
||||
/**
|
||||
* Set to either the values map or the BasinMapFactory that was used to
|
||||
* create it to enable correct synchronization.
|
||||
*/
|
||||
@Transient
|
||||
protected Object valuesSynchronization;
|
||||
|
||||
/** object used for serialization **/
|
||||
@DynamicSerializeElement
|
||||
public float[] serializedValues;
|
||||
|
@ -172,9 +182,9 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
Date prevDate = null;
|
||||
|
||||
// map ordered newest first, so grab from newest date to oldest date
|
||||
if (afterDate.before(beforeDate) && (values.size() > 0)) {
|
||||
if (afterDate.before(beforeDate) && (!values.isEmpty())) {
|
||||
|
||||
synchronized (values) {
|
||||
synchronized (valuesSynchronization) {
|
||||
|
||||
float factor = 0.0f;
|
||||
|
||||
|
@ -226,7 +236,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
*/
|
||||
public Float getValue(Date afterDate, Date beforeDate) {
|
||||
Float val = 0.0f;
|
||||
synchronized (values) {
|
||||
synchronized (valuesSynchronization) {
|
||||
Date checkDate = values.ceilingKey(afterDate);
|
||||
if ((checkDate != null) && checkDate.before(beforeDate)) {
|
||||
val = values.get(checkDate);
|
||||
|
@ -260,7 +270,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
Float val = 0.0f;
|
||||
int i = 0;
|
||||
|
||||
synchronized (values) {
|
||||
synchronized (valuesSynchronization) {
|
||||
|
||||
for (Date date : values.keySet()) {
|
||||
if (date.before(beforeDate) && date.after(afterDate)) {
|
||||
|
@ -290,7 +300,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
public Float getMaxValue(Date afterDate, Date beforeDate) {
|
||||
Float val = 0.0f;
|
||||
|
||||
synchronized (values) {
|
||||
synchronized (valuesSynchronization) {
|
||||
|
||||
for (Date date : values.keySet()) {
|
||||
if (date.before(beforeDate) && date.after(afterDate)) {
|
||||
|
@ -312,13 +322,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
* @param value
|
||||
*/
|
||||
public void setValue(Date date, Float dvalue) {
|
||||
synchronized (values) {
|
||||
if (!(values instanceof TreeMap) && !values.containsKey(dvalue)) {
|
||||
// ArrayBackedMap may have been used if this basin was
|
||||
// deserialized. It is much faster to do inserts on a TreeMap so
|
||||
// convert now.
|
||||
values = new TreeMap<Date, Float>(values);
|
||||
}
|
||||
synchronized (valuesSynchronization) {
|
||||
values.put(date, dvalue);
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +343,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
*/
|
||||
public void setValues(TreeMap<Date, Float> values) {
|
||||
this.values = values;
|
||||
this.valuesSynchronization = values;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,6 +352,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
public FFMPBasin() {
|
||||
|
||||
values = new TreeMap<Date, Float>(Collections.reverseOrder());
|
||||
valuesSynchronization = values;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,6 +364,15 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
setPfaf(pfaf);
|
||||
setAggregated(aggregated);
|
||||
values = new TreeMap<Date, Float>(Collections.reverseOrder());
|
||||
valuesSynchronization = values;
|
||||
}
|
||||
|
||||
public FFMPBasin(Long pfaf, boolean aggregated,
|
||||
BasinMapFactory<Date> mapFactory) {
|
||||
setPfaf(pfaf);
|
||||
setAggregated(aggregated);
|
||||
values = mapFactory.getMap();
|
||||
valuesSynchronization = mapFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -365,16 +380,16 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
*
|
||||
* @param times
|
||||
*/
|
||||
public void deserialize(long[] times) {
|
||||
public void deserialize(long[] times, BasinMapFactory<Date> mapFactory) {
|
||||
// safe to avoid Array Index Exceptions / shouldn't happen but.....
|
||||
|
||||
if (serializedValues != null
|
||||
&& (times.length == serializedValues.length)) {
|
||||
NavigableMap<Date, Float> fastMap = new ArrayBackedMap(times,
|
||||
serializedValues);
|
||||
values = fastMap.descendingMap();
|
||||
|
||||
// values = new TreeMap<Date, Float>(fastMap.descendingMap());
|
||||
values = mapFactory.getMap(fastMap.descendingMap());
|
||||
valuesSynchronization = mapFactory;
|
||||
}
|
||||
serializedValues = null;
|
||||
}
|
||||
|
@ -401,7 +416,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
|
|||
*/
|
||||
public void purgeData(Date date) {
|
||||
if (values != null) {
|
||||
synchronized (values) {
|
||||
synchronized (valuesSynchronization) {
|
||||
ArrayList<Date> removes = new ArrayList<Date>();
|
||||
for (Date mdate : values.keySet()) {
|
||||
if (mdate.before(date)) {
|
||||
|
|
|
@ -22,6 +22,7 @@ package com.raytheon.uf.common.dataplugin.ffmp;
|
|||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -29,6 +30,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import com.raytheon.uf.common.dataplugin.ffmp.FFMPDataRecordLoader.LoadTask;
|
||||
import com.raytheon.uf.common.dataplugin.ffmp.collections.BasinMapFactory;
|
||||
import com.raytheon.uf.common.datastorage.DataStoreFactory;
|
||||
import com.raytheon.uf.common.datastorage.records.FloatDataRecord;
|
||||
import com.raytheon.uf.common.monitor.config.FFMPSourceConfigurationManager;
|
||||
|
@ -55,6 +57,8 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
|
|||
* 07/09/13 2152 njensen Ensure purgeData() does not load data
|
||||
* Jul 15, 2013 2184 dhladky Remove all HUC's for storage except ALL
|
||||
* 07/16/13 2197 njensen Added hasAnyBasins() and moved getBasins() calls out of loops
|
||||
* Jul 31, 2013 2242 bsteffen Optimize FFMP NavigableMap memory.
|
||||
*
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -83,6 +87,20 @@ public class FFMPBasinData implements ISerializableObject {
|
|||
*/
|
||||
private final Map<String, FFMPBasin[]> orderedBasinsCache = new HashMap<String, FFMPBasin[]>();
|
||||
|
||||
/**
|
||||
* Shared factory for efficient storage of data in basins.
|
||||
*/
|
||||
private BasinMapFactory<Date> mapFactory = null;
|
||||
|
||||
/**
|
||||
* Public one arg constructor
|
||||
*
|
||||
* @param huc_level
|
||||
*/
|
||||
public FFMPBasinData(String hucLevel) {
|
||||
setHucLevel(hucLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* No arg hibernate constructor
|
||||
*/
|
||||
|
@ -673,12 +691,17 @@ public class FFMPBasinData implements ISerializableObject {
|
|||
*/
|
||||
public void populate(List<Long> times) {
|
||||
|
||||
if (mapFactory == null) {
|
||||
mapFactory = new BasinMapFactory<Date>(Collections.reverseOrder(),
|
||||
getBasins().size());
|
||||
}
|
||||
|
||||
long[] timesArr = new long[times.size()];
|
||||
for (int i = 0; i < timesArr.length; i += 1) {
|
||||
timesArr[i] = times.get(i);
|
||||
}
|
||||
for (FFMPBasin basin : getBasins().values()) {
|
||||
basin.deserialize(timesArr);
|
||||
basin.deserialize(timesArr, mapFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -736,7 +759,12 @@ public class FFMPBasinData implements ISerializableObject {
|
|||
if (guidance) {
|
||||
basin = new FFMPGuidanceBasin(pfaf, aggregate);
|
||||
} else {
|
||||
basin = new FFMPBasin(pfaf, aggregate);
|
||||
if (mapFactory == null) {
|
||||
mapFactory = new BasinMapFactory<Date>(
|
||||
Collections.reverseOrder(),
|
||||
orderedPfafs.size());
|
||||
}
|
||||
basin = new FFMPBasin(pfaf, aggregate, mapFactory);
|
||||
}
|
||||
this.basins.put(pfaf, basin);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.uf.common.dataplugin.ffmp;
|
||||
package com.raytheon.uf.common.dataplugin.ffmp.collections;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
|
@ -34,7 +34,6 @@ import java.util.NavigableSet;
|
|||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
|
@ -67,6 +66,8 @@ import java.util.TreeMap;
|
|||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 18, 2013 bsteffen Initial creation
|
||||
* Jul 31, 2013 2242 bsteffen Extracted NavigableKeySet to its own
|
||||
* file and moved to collections package.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -264,7 +265,7 @@ public class ArrayBackedMap extends AbstractMap<Date, Float> implements
|
|||
|
||||
@Override
|
||||
public NavigableSet<Date> navigableKeySet() {
|
||||
return new NavigableKeySet(this);
|
||||
return new NavigableKeySet<Date>(this);
|
||||
}
|
||||
|
||||
private int lowerIndex(Date key, boolean inclusive) {
|
||||
|
@ -592,113 +593,7 @@ public class ArrayBackedMap extends AbstractMap<Date, Float> implements
|
|||
|
||||
}
|
||||
|
||||
private static class NavigableKeySet extends AbstractSet<Date> implements
|
||||
NavigableSet<Date> {
|
||||
|
||||
private final NavigableMap<Date, Float> map;
|
||||
|
||||
public NavigableKeySet(NavigableMap<Date, Float> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Date> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super Date> comparator() {
|
||||
return map.comparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date first() {
|
||||
return map.firstKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date last() {
|
||||
return map.lastKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date lower(Date e) {
|
||||
return map.lowerKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date floor(Date e) {
|
||||
return map.floorKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date ceiling(Date e) {
|
||||
return map.ceilingKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date higher(Date e) {
|
||||
return map.higherKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date pollFirst() {
|
||||
return map.pollFirstEntry().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date pollLast() {
|
||||
return map.pollLastEntry().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Date> descendingSet() {
|
||||
return map.descendingKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Date> descendingIterator() {
|
||||
return descendingSet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Date> subSet(Date fromElement,
|
||||
boolean fromInclusive, Date toElement, boolean toInclusive) {
|
||||
return map.subMap(fromElement, fromInclusive, toElement,
|
||||
toInclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Date> headSet(Date toElement, boolean inclusive) {
|
||||
return map.headMap(toElement, inclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Date> tailSet(Date fromElement, boolean inclusive) {
|
||||
return map.tailMap(fromElement, inclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Date> subSet(Date fromElement, Date toElement) {
|
||||
return subSet(fromElement, true, toElement, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Date> headSet(Date toElement) {
|
||||
return headSet(toElement, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Date> tailSet(Date fromElement) {
|
||||
return tailSet(fromElement, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class SubMap extends AbstractMap<Date, Float> implements
|
||||
NavigableMap<Date, Float> {
|
||||
|
@ -854,7 +749,7 @@ public class ArrayBackedMap extends AbstractMap<Date, Float> implements
|
|||
|
||||
@Override
|
||||
public NavigableSet<Date> navigableKeySet() {
|
||||
return new NavigableKeySet(this);
|
||||
return new NavigableKeySet<Date>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1066,7 +961,7 @@ public class ArrayBackedMap extends AbstractMap<Date, Float> implements
|
|||
|
||||
@Override
|
||||
public NavigableSet<Date> navigableKeySet() {
|
||||
return new NavigableKeySet(this);
|
||||
return new NavigableKeySet<Date>(this);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,621 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.uf.common.dataplugin.ffmp.collections;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Factory for constructing maps to use in FFMPBasins for organizing values.
|
||||
*
|
||||
* Each basin in FFMP requires a NavigableMap<Date,Float> in order to achieve
|
||||
* fast dynamic calculations. Unfortunately due to the large number of basins
|
||||
* TreeMaps require too much memory. This factory produces maps that have the
|
||||
* same performance as TreeMaps but are much more memory efficient.
|
||||
*
|
||||
* This factory achieves efficiency by taking advantage of the fact that all
|
||||
* basins will have data for the same times, so it can use a single TreeMap as
|
||||
* the backing map for all basins. Each NavigableMap returned from this factory
|
||||
* is simply a view into the backing map.
|
||||
*
|
||||
* Because all maps returned from this factory use the same backing map, they
|
||||
* cannot be synchronized individually. Internally all map modifications are
|
||||
* synchronized on the factory object to prevent concurrent modification. If any
|
||||
* iterators are being used from any maps then the iteration should also be
|
||||
* synchronized on the factory.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jul 30, 2013 2242 bsteffen Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bsteffen
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class BasinMapFactory<K> {
|
||||
|
||||
/*
|
||||
* How much to grow the backing arrays to make room for new maps, this is
|
||||
* much more conservative than the algorithms used by ArrayList or HashMap
|
||||
* because typically growth will occur very briefly during initialization
|
||||
* and the extra overhead of a slower growth rate is minimal. Once all
|
||||
* basins are created, growth will stop and a conservative growth algorithm
|
||||
* limits wasted memory after the initial growth phase.
|
||||
*/
|
||||
private static final int GROWTH_RATE = 32;
|
||||
|
||||
/* The amount of space allocated in each value. */
|
||||
protected volatile int allocated_size;
|
||||
|
||||
/* The number of indices used in maps so far. */
|
||||
private volatile int used_size;
|
||||
|
||||
/* This is the real navigable map, responsible for all real work. */
|
||||
private final NavigableMap<K, MultiValue> backingMap;
|
||||
|
||||
/**
|
||||
* Create a new factory
|
||||
*
|
||||
* @param comparator
|
||||
* the comparator to use for map keys
|
||||
* @param initialSize
|
||||
* the amount of space to preallocate for basin maps.
|
||||
*/
|
||||
public BasinMapFactory(Comparator<? super K> comparator, int initialSize) {
|
||||
allocated_size = initialSize;
|
||||
used_size = 0;
|
||||
this.backingMap = new TreeMap<K, MultiValue>(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new map from this factory.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public NavigableMap<K, Float> getMap() {
|
||||
return new MapView<K>(this, getNextIndex(),
|
||||
backingMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new map from this factory, populated with all the values from m. If
|
||||
* m has the same keyset as other maps in this factory than this will
|
||||
* populate the new map faster than putAll.
|
||||
*
|
||||
* @param m
|
||||
* NavigableMap, must have the same comparator as this factory.
|
||||
* @return
|
||||
*/
|
||||
public NavigableMap<K, Float> getMap(NavigableMap<K, Float> m) {
|
||||
Comparator<? super K> bc = backingMap.comparator();
|
||||
Comparator<? super K> mc = m.comparator();
|
||||
if (mc != bc && (mc == null || !mc.equals(bc))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Maps can only be constructed if the compators are the same.");
|
||||
}
|
||||
int index = getNextIndex();
|
||||
Iterator<Entry<K, MultiValue>> bit = backingMap.entrySet().iterator();
|
||||
Iterator<Entry<K, Float>> mit = m.entrySet().iterator();
|
||||
NavigableMap<K, Float> r = new MapView<K>(this, index,
|
||||
backingMap);
|
||||
/*
|
||||
* If both maps have the same keys, then iterating both simultaneously
|
||||
* is faster than multiple puts because it avoids doing multiple lookups
|
||||
*/
|
||||
while (bit.hasNext() && mit.hasNext()) {
|
||||
Entry<K, MultiValue> bent = bit.next();
|
||||
Entry<K, Float> ment = mit.next();
|
||||
if (ment.getKey().equals(bent.getKey())) {
|
||||
bent.getValue().put(index, ment.getValue());
|
||||
} else {
|
||||
/* It turns out keys are not equals */
|
||||
r.put(ment.getKey(), ment.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This loop is only used if the backingMap was empty or if for some
|
||||
* reason the backingMap and the new map do not have the same keys.
|
||||
*/
|
||||
while (mit.hasNext()) {
|
||||
Entry<K, Float> ment = mit.next();
|
||||
r.put(ment.getKey(), ment.getValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* get the next free index for use in a MapView */
|
||||
private int getNextIndex() {
|
||||
synchronized (this) {
|
||||
int index = used_size;
|
||||
used_size += 1;
|
||||
if (used_size >= allocated_size) {
|
||||
allocated_size += GROWTH_RATE;
|
||||
for (MultiValue v : backingMap.values()) {
|
||||
v.grow(allocated_size);
|
||||
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Value within the backing map. This contains the raw data values for each
|
||||
* basin. In addition it contains a boolean for each basin indicating if the
|
||||
* value has been set. While most of the time all basins will be set, the
|
||||
* extra boolean is needed to allow different views to function as
|
||||
* independent maps, which is needed while new data is getting added.
|
||||
*/
|
||||
private static class MultiValue {
|
||||
|
||||
/* actual values for all basins. */
|
||||
private float[] data;
|
||||
|
||||
/* booleans indicating whether a value has been set yet for a basin */
|
||||
private BitSet occupied;
|
||||
|
||||
/*
|
||||
* The number of occupied basins for this time, when its zero the time
|
||||
* can be removed from the backing map.
|
||||
*/
|
||||
private int size = 0;
|
||||
|
||||
public MultiValue(int allocated_size) {
|
||||
this.data = new float[allocated_size];
|
||||
this.occupied = new BitSet(allocated_size);
|
||||
}
|
||||
|
||||
public void grow(int allocated_size) {
|
||||
data = Arrays.copyOf(data, allocated_size);
|
||||
BitSet occupied = new BitSet(allocated_size);
|
||||
occupied.or(this.occupied);
|
||||
this.occupied = occupied;
|
||||
}
|
||||
|
||||
public Float put(int index, float value) {
|
||||
Float oldValue = null;
|
||||
if (occupied.get(index)) {
|
||||
oldValue = data[index];
|
||||
} else {
|
||||
occupied.set(index);
|
||||
size += 1;
|
||||
}
|
||||
data[index] = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public Float get(int index) {
|
||||
if (occupied.get(index)) {
|
||||
return data[index];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(int index) {
|
||||
return occupied.get(index);
|
||||
}
|
||||
|
||||
public Float remove(int index) {
|
||||
if (occupied.get(index)) {
|
||||
occupied.clear(index);
|
||||
size -= 1;
|
||||
return data[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* NavigableMap implementation which provides a view into the backingMap at
|
||||
* a specific index. This class can map any NavigableMap<K, MultiValue>
|
||||
* which simplifies creating sub maps or descending map because all that is
|
||||
* required is getting new maps from the backingMap and wrapping them in a
|
||||
* new MapView.
|
||||
*/
|
||||
private static class MapView<K> extends AbstractMap<K, Float> implements
|
||||
NavigableMap<K, Float> {
|
||||
|
||||
/* Factory, for syncronization */
|
||||
private final BasinMapFactory<K> factory;
|
||||
|
||||
/* index into the MultiValue */
|
||||
private final int index;
|
||||
|
||||
/* backingMap where all the data really lives. */
|
||||
private final NavigableMap<K, MultiValue> backingMap;
|
||||
|
||||
public MapView(BasinMapFactory<K> factory, int index,
|
||||
NavigableMap<K, MultiValue> backingMap) {
|
||||
this.factory = factory;
|
||||
this.index = index;
|
||||
this.backingMap = backingMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float put(K key, Float value) {
|
||||
synchronized (factory) {
|
||||
MultiValue v = backingMap.get(key);
|
||||
if (v == null) {
|
||||
v = new MultiValue(factory.allocated_size);
|
||||
backingMap.put(key, v);
|
||||
}
|
||||
return v.put(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
MultiValue v = backingMap.get(key);
|
||||
if (v != null) {
|
||||
return v.contains(index);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(Object key) {
|
||||
MultiValue v = backingMap.get(key);
|
||||
if (v != null) {
|
||||
return v.get(index);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float remove(Object key) {
|
||||
Float oldValue = null;
|
||||
MultiValue v = backingMap.get(key);
|
||||
if (v != null) {
|
||||
synchronized (factory) {
|
||||
oldValue = v.remove(index);
|
||||
if (v.isEmpty()) {
|
||||
backingMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
/*
|
||||
* The default implementation uses size() which will iterate over
|
||||
* all elements, this is much faster.
|
||||
*/
|
||||
return !entrySet().iterator().hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super K> comparator() {
|
||||
return backingMap.comparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K firstKey() {
|
||||
Entry<K, Float> e = firstEntry();
|
||||
if (e != null) {
|
||||
return e.getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lastKey() {
|
||||
Entry<K, Float> e = lastEntry();
|
||||
if (e != null) {
|
||||
return e.getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> lowerEntry(K key) {
|
||||
return headMap(key).lastEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lowerKey(K key) {
|
||||
return headMap(key).lastKey();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> floorEntry(K key) {
|
||||
return headMap(key, true).lastEntry();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public K floorKey(K key) {
|
||||
return headMap(key, true).lastKey();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> ceilingEntry(K key) {
|
||||
return tailMap(key).firstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K ceilingKey(K key) {
|
||||
return tailMap(key).firstKey();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> higherEntry(K key) {
|
||||
return tailMap(key, false).firstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K higherKey(K key) {
|
||||
return tailMap(key, false).firstKey();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> firstEntry() {
|
||||
for (Entry<K, Float> e : entrySet()) {
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> lastEntry() {
|
||||
return descendingMap().firstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> pollFirstEntry() {
|
||||
Iterator<Entry<K, Float>> it = entrySet().iterator();
|
||||
if (it.hasNext()) {
|
||||
Entry<K, Float> e = it.next();
|
||||
it.remove();
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> pollLastEntry() {
|
||||
return descendingMap().pollFirstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> descendingMap() {
|
||||
return new MapView<K>(factory, index, backingMap.descendingMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> navigableKeySet() {
|
||||
return new NavigableKeySet<K>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> descendingKeySet() {
|
||||
return descendingMap().navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> subMap(K fromKey, boolean fromInclusive,
|
||||
K toKey, boolean toInclusive) {
|
||||
return new MapView<K>(factory, index, backingMap.subMap(fromKey,
|
||||
fromInclusive, toKey, toInclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> headMap(K toKey, boolean inclusive) {
|
||||
return new MapView<K>(factory, index, backingMap.headMap(toKey,
|
||||
inclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> tailMap(K fromKey, boolean inclusive) {
|
||||
return new MapView<K>(factory, index, backingMap.tailMap(fromKey,
|
||||
inclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> subMap(K fromKey, K toKey) {
|
||||
return new MapView<K>(factory, index, backingMap.subMap(fromKey,
|
||||
true, toKey, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> headMap(K toKey) {
|
||||
return new MapView<K>(factory, index, backingMap.headMap(toKey,
|
||||
false));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, Float> tailMap(K fromKey) {
|
||||
return new MapView<K>(factory, index, backingMap.headMap(fromKey,
|
||||
true));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, Float>> entrySet() {
|
||||
return new EntrySet<K>(index, backingMap.entrySet());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry set for a MapView, just a wrapper over a Set<Entry<K, MultiValue>>
|
||||
*/
|
||||
private static class EntrySet<K> extends AbstractSet<Entry<K, Float>> {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final Set<Entry<K, MultiValue>> backingSet;
|
||||
|
||||
public EntrySet(int index, Set<Entry<K, MultiValue>> backingSet) {
|
||||
this.index = index;
|
||||
this.backingSet = backingSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, Float>> iterator() {
|
||||
return new EntryIterator<K>(index, backingSet.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
Iterator<Entry<K, Float>> it = iterator();
|
||||
int i = 0;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
i += 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator implementation for an EntrySet, This wraps an Iterator<Entry<K,
|
||||
* MultiValue>> but has extra logic for skipping over values that exist in
|
||||
* the backingSet but are not occupied within the MultiValue.
|
||||
*/
|
||||
private static class EntryIterator<K> implements Iterator<Entry<K, Float>> {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final Iterator<Entry<K, MultiValue>> backingIterator;
|
||||
|
||||
/* The next valid entry in the backingMap */
|
||||
private transient Entry<K, MultiValue> next;
|
||||
|
||||
/* Previous Value returned by next() */
|
||||
private transient Entry<K, MultiValue> previous;
|
||||
|
||||
public EntryIterator(int index,
|
||||
Iterator<Entry<K, MultiValue>> backingIterator) {
|
||||
this.index = index;
|
||||
this.backingIterator = backingIterator;
|
||||
}
|
||||
|
||||
private Entry<K, MultiValue> checkNext() {
|
||||
if (next == null) {
|
||||
while (backingIterator.hasNext()) {
|
||||
Entry<K, MultiValue> e = backingIterator.next();
|
||||
previous = null;
|
||||
MultiValue v = e.getValue();
|
||||
if (v.contains(index)) {
|
||||
next = e;
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return checkNext() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, Float> next() {
|
||||
Entry<K, MultiValue> next = checkNext();
|
||||
previous = next;
|
||||
if (next == null) {
|
||||
return null;
|
||||
} else {
|
||||
this.next = null;
|
||||
return new EntryImpl<K>(index, next);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (previous == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot remove from iterator because next() was not called.");
|
||||
}
|
||||
MultiValue v = previous.getValue();
|
||||
previous = null;
|
||||
v.remove(index);
|
||||
if (v.isEmpty()) {
|
||||
backingIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry for a MapView, just a wrapper over a Entry<K, MultiValue>.
|
||||
*/
|
||||
private static class EntryImpl<K> implements Entry<K, Float> {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final Entry<K, MultiValue> backingEntry;
|
||||
|
||||
public EntryImpl(int index, Entry<K, MultiValue> timeEntry) {
|
||||
this.index = index;
|
||||
this.backingEntry = timeEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return backingEntry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getValue() {
|
||||
return backingEntry.getValue().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float setValue(Float value) {
|
||||
return backingEntry.getValue().put(index, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.uf.common.dataplugin.ffmp.collections;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic NavigableSet which is implemented by wrapping a fully implemented
|
||||
* NavigableMap. Very useful for custom NavigableMap implementations.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jul 31, 2013 2242 bsteffen Extracted from ArrayBackedMap
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bsteffen
|
||||
* @version 1.0
|
||||
* @param <K>
|
||||
*/
|
||||
class NavigableKeySet<K> extends AbstractSet<K> implements NavigableSet<K> {
|
||||
|
||||
private final NavigableMap<K, ?> map;
|
||||
|
||||
public NavigableKeySet(NavigableMap<K, ?> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<K> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super K> comparator() {
|
||||
return map.comparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K first() {
|
||||
return map.firstKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K last() {
|
||||
return map.lastKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lower(K e) {
|
||||
return map.lowerKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K floor(K e) {
|
||||
return map.floorKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K ceiling(K e) {
|
||||
return map.ceilingKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K higher(K e) {
|
||||
return map.higherKey(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K pollFirst() {
|
||||
return map.pollFirstEntry().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K pollLast() {
|
||||
return map.pollLastEntry().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> descendingSet() {
|
||||
return map.descendingKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<K> descendingIterator() {
|
||||
return descendingSet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> subSet(K fromElement, boolean fromInclusive,
|
||||
K toElement, boolean toInclusive) {
|
||||
return map.subMap(fromElement, fromInclusive, toElement,
|
||||
toInclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> headSet(K toElement, boolean inclusive) {
|
||||
return map.headMap(toElement, inclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> tailSet(K fromElement, boolean inclusive) {
|
||||
return map.tailMap(fromElement, inclusive).navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<K> subSet(K fromElement, K toElement) {
|
||||
return subSet(fromElement, true, toElement, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<K> headSet(K toElement) {
|
||||
return headSet(toElement, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<K> tailSet(K fromElement) {
|
||||
return tailSet(fromElement, true);
|
||||
}
|
||||
|
||||
}
|
|
@ -52,6 +52,7 @@ import com.vividsolutions.jts.geom.Geometry;
|
|||
* 03/12/2007 1003 bwoodle initial creation
|
||||
* 04/12/2013 1857 bgonzale Added SequenceGenerator annotation.
|
||||
* 05/02/2013 1949 rjpeter Moved ugcZones to be a column inside table.
|
||||
* 08/08/2013 2243 jsanchez Removed super method in copy constructor.
|
||||
* </pre>
|
||||
*
|
||||
* @author bwoodle
|
||||
|
@ -233,7 +234,6 @@ public abstract class AbstractWarningRecord extends PluginDataObject {
|
|||
* The text of the message
|
||||
*/
|
||||
public AbstractWarningRecord(AbstractWarningRecord old) {
|
||||
super((String) old.getMessageData());
|
||||
this.setCountyheader(old.getCountyheader());
|
||||
this.setDataTime(old.getDataTime());
|
||||
this.setForecaster(old.getForecaster());
|
||||
|
|
Binary file not shown.
Loading…
Add table
Reference in a new issue