Merge "Issue #1126 Refactored locking and wrapping Change-Id: I8053e8a8562eba9224a032c482f1cbca5af01df0" into development

Former-commit-id: 82248ebbd0 [formerly d29865eccc] [formerly 82248ebbd0 [formerly d29865eccc] [formerly 51b63c1667 [formerly 4d389f0276dd843fac7fdf4d680be188a0ebf815]]]
Former-commit-id: 51b63c1667
Former-commit-id: 0081b12244 [formerly 6c1c43333b]
Former-commit-id: 5fd0c87652
This commit is contained in:
Richard Peter 2012-09-27 15:49:06 -05:00 committed by Gerrit Code Review
commit dee969da7d
13 changed files with 1478 additions and 990 deletions

View file

@ -93,11 +93,12 @@ import com.raytheon.viz.warngen.gui.BackupData;
import com.raytheon.viz.warngen.gui.FollowupData;
import com.raytheon.viz.warngen.gui.WarngenLayer;
import com.raytheon.viz.warngen.gui.WarngenUIState;
import com.raytheon.viz.warngen.text.WarningTextHandler;
import com.raytheon.viz.warngen.text.WarningTextHandlerFactory;
import com.raytheon.viz.warngen.util.CurrentWarnings;
import com.raytheon.viz.warngen.util.FipsUtil;
import com.raytheon.viz.warngen.util.FollowUpUtil;
import com.raytheon.viz.warngen.util.WarnGenMathTool;
import com.raytheon.viz.warngen.util.WarningTextHandler;
import com.raytheon.viz.warngen.util.WatchUtil;
import com.raytheon.viz.warngen.util.WeatherAdvisoryWatch;
import com.raytheon.viz.warnings.DateUtil;
@ -126,7 +127,8 @@ import com.vividsolutions.jts.io.WKTReader;
* from appearing in 2nd and 3rd bullets when not necessary.
* Aug 13, 2012 14493 Qinglu Lin Handled MND time, event time, and TML time specially for COR to NEW.
* Aug 29, 2011 15351 jsanchez Set the timezone for TML time.
* Sep 10, 2012 15295 snaples Added property setting for runtime log to createScript.
* Sep 10, 2012 15295 snaples Added property setting for runtime log to createScript.
* Sep 18, 2012 15332 jsanchez Used a new warning text handler.
* </pre>
*
* @author njensen
@ -760,16 +762,19 @@ public class TemplateRunner {
System.out.println("velocity time: "
+ (System.currentTimeMillis() - tz0));
return WarningTextHandler.handle(script.toString().toUpperCase(),
areas, cancelareas, selectedAction,
WarningAction.valueOf((String) context.get("action")),
config.getAutoLockText());
String text = script.toString();
WarningTextHandler handler = WarningTextHandlerFactory.getHandler(
selectedAction, text, config.getAutoLockText());
String handledText = handler.handle(text, areas, cancelareas);
return handledText;
}
private static VelocityEngine ENGINE = new VelocityEngine();
private static synchronized String createScript(String vmFile,
VelocityContext context, String site, String logDir) throws EdexException {
VelocityContext context, String site, String logDir)
throws EdexException {
StringWriter sw = new StringWriter();
try {
Properties p = new Properties();
@ -780,8 +785,7 @@ public class TemplateRunner {
p.setProperty(
"velocimacro.permissions.allow.inline.to.replace.global",
"true");
p.setProperty("runtime.log",
FileUtil.join(logDir, "velocity.log"));
p.setProperty("runtime.log", FileUtil.join(logDir, "velocity.log"));
ENGINE.init(p);
context.put("scriptLibrary", "VM_global_library.vm");
Template template = ENGINE.getTemplate(vmFile,

View file

@ -0,0 +1,357 @@
/**
* 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.viz.warngen.text;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.texteditor.util.VtecUtil;
import com.raytheon.viz.warngen.gis.AffectedAreas;
/**
* Abstract class that locks certain text patterns in the warning text.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
abstract public class AbstractLockingBehavior implements ICommonPatterns {
/**
* Sorts in reverse order the areas based on the length of the name.
*
* @author jsanchez
*
*/
private class AreaLockingComparator implements Comparator<AffectedAreas> {
@Override
public int compare(AffectedAreas o1, AffectedAreas o2) {
Integer i1 = new Integer(o1.getName().length());
Integer i2 = new Integer(o2.getName().length());
return i2.compareTo(i1);
}
}
protected List<AffectedAreas> affectedAreas;
protected String text;
protected static final String REPLACEMENT = LOCK_START + "$0" + LOCK_END;
protected String warningType = "(FLOOD ADVISORY)|(FLOOD WARNING)|(FLOOD STATEMENT)"
+ "|(SEVERE WEATHER STATEMENT)|(EXTREME WIND WARNING)|(FIRE WARNING)"
+ "|(FLASH FLOOD WARNING)|(FLASH FLOOD STATEMENT)|(SEVERE THUNDERSTORM WARNING)"
+ "|(TORNADO WARNING)|(MARINE WEATHER STATEMENT)|(SHORT TERM FORECAST)"
+ "|(SPECIAL WEATHER STATEMENT)|(SPECIAL MARINE WARNING)";
/**
* Locks the particular strings in the texts based on common patterns and
* the affected areas.
*
* @param text
* @param affectedAreas
* @param canceledAreas
* @return
*/
public String lock(String text, AffectedAreas[] affectedAreas,
AffectedAreas[] canceledAreas) {
this.text = text;
intialize(affectedAreas, canceledAreas);
ugc();
htec();
vtec();
areaNames();
mnd();
date();
body();
callToActions();
latLon();
tml();
testMessages();
clean();
return this.text;
}
/**
* Calls the sub classes implementation of the body method.
*/
abstract protected void body();
/**
* Adds the affect areas into the appropriate lists.
*
* @param affectedAreas
* @param canceledAreas
*/
private void intialize(AffectedAreas[] affectedAreas,
AffectedAreas[] canceledAreas) {
AreaLockingComparator comparator = new AreaLockingComparator();
if (affectedAreas != null) {
this.affectedAreas = new ArrayList<AffectedAreas>(
Arrays.asList(affectedAreas));
} else {
this.affectedAreas = new ArrayList<AffectedAreas>(0);
}
if (canceledAreas != null) {
this.affectedAreas.addAll(Arrays.asList(canceledAreas));
}
Collections.sort(this.affectedAreas, comparator);
}
/**
* Locks the UGC line or FIPS line.
*/
private void ugc() {
Pattern ugcPtrn = Pattern.compile(ugc + newline, Pattern.MULTILINE);
find(ugcPtrn.matcher(text));
}
/**
* Locks the HTEC line.
*/
private void htec() {
// LOCK_END can be added at the start of the line if a previous line has
// been locked.
String htec = "^(("
+ LOCK_END
+ "){0,1}/[A-Za-z0-9]{5}.[0-3NU].\\w{2}.\\d{6}T\\d{4}Z.\\d{6}T\\d{4}Z.\\d{6}T\\d{4}Z.\\w{2}/"
+ newline + ")";
Pattern htecPtrn = Pattern.compile(htec, Pattern.MULTILINE);
find(htecPtrn.matcher(text));
}
/**
* Locks the VTEC line.
*/
private void vtec() {
// LOCK_END can be added at the start of the line if a previous line has
// been locked.
String vtec = "^(("
+ LOCK_END
+ "){0,1}/[OTEX]\\.([A-Z]{3})\\.[A-Za-z0-9]{4}\\.[A-Z]{2}\\.[WAYSFON]\\.\\d{4}\\.\\d{6}T\\d{4}Z-\\d{6}T\\d{4}Z/"
+ newline + ")";
Pattern vtecPtrn = Pattern.compile(vtec, Pattern.MULTILINE);
find(vtecPtrn.matcher(text));
}
/**
* Locks the list of area names.
*/
private void areaNames() {
find(listOfAreaNamePtrn.matcher(text));
}
/**
* Helper method to replace the found patterns.
*
* @param m
*/
protected void find(Matcher m) {
if (m.find()) {
String group = m.group();
// If line already starts with a LOCK_END, the LOCK_END is removed
// and the line just adds to the LOCK_END at the end.
if (group.startsWith(LOCK_END)) {
this.text = text.replace(group,
group.substring(LOCK_END.length()) + LOCK_END);
} else {
this.text = m.replaceAll(LOCK_START + "$0" + LOCK_END);
}
}
}
/**
* This method locks the entire MND Header block.
*/
private void mnd() {
int start = -1;
int end = -1;
StringBuffer mnd = new StringBuffer(
"(BULLETIN - IMMEDIATE BROADCAST REQUESTED)|(BULLETIN - EAS ACTIVATION REQUESTED)|"
+ warningType);
Pattern startMND = Pattern.compile(mnd.toString());
Matcher m = startMND.matcher(text);
if (m.find()) {
start = m.start();
}
m = datePtrn.matcher(text);
if (m.find()) {
end = m.start();
}
if (start != -1 && end != -1 && start < end) {
String substring = text.substring(start, end);
// There should be only one instance of an MND Header block
this.text = text.replace(substring, LOCK_START + substring
+ LOCK_END);
}
}
/**
* Locks the date.
*/
private void date() {
find(datePtrn.matcher(text));
}
/**
* Locks the TIME...MOT...LINE (Can be multiple lines).
*/
private void tml() {
// LOCK_END can be added at the start of the line if a previous line has
// been locked.
String tml = "^(("
+ LOCK_END
+ "){0,1}(TIME\\.\\.\\.MOT\\.\\.\\.LOC \\d{3,4}Z \\d{3}DEG \\d{1,3}KT(( \\d{3,4} \\d{3,5}){1,})(\\s*\\d{3,5} )*)\\s*"
+ newline + ")";
Pattern tmlPtrn = Pattern.compile(tml, Pattern.MULTILINE);
find(tmlPtrn.matcher(text));
}
/**
* Locks the coordinates of the polygon.
*/
private void latLon() {
// LOCK_END should not be found at the beginning of the LAT...LON since
// the previous line should be blank.
String latLon = "^((LAT\\.\\.\\.LON( \\d{3,4} \\d{3,5})+)" + newline
+ ")(((\\s{5}( \\d{3,4} \\d{3,5})+)" + newline + ")+)?";
Pattern latLonPtrn = Pattern.compile(latLon, Pattern.MULTILINE);
find(latLonPtrn.matcher(text));
}
/**
* Locks the Call To Action header and the segment tags.
*/
private void callToActions() {
// LOCK_END should not be found at the beginning since the previous line
// should be blank.
String precautionaryPtrn = "^(PRECAUTIONARY/PREPAREDNESS ACTIONS\\.\\.\\."
+ newline + ")";
String ctaEndPtrn = "^(&&" + newline + ")";
String segmentPtrn = "^(\\$\\$" + newline + ")";
Pattern cta = Pattern.compile("(" + precautionaryPtrn + ")" + "|("
+ ctaEndPtrn + ")" + "|(" + segmentPtrn + ")",
Pattern.MULTILINE);
find(cta.matcher(text));
}
/**
* Locks the test messages.
*/
private void testMessages() {
String test1 = "THIS IS A TEST MESSAGE\\. DO NOT TAKE ACTION BASED ON THIS MESSAGE\\."
+ newline;
String test2 = "THIS IS A TEST MESSAGE\\.";
String test3 = "\\.\\.\\.THIS MESSAGE IS FOR TEST PURPOSES ONLY\\.\\.\\."
+ newline;
Pattern testPtrn = Pattern.compile("(" + test1 + ")|" + "(" + test2
+ ")|" + "(" + test3 + ")");
find(testPtrn.matcher(text));
}
private void clean() {
// where a lock close and lock open are only separated by whitespace
// remove the close and open to join the two locked areas
text = text.replaceAll("</L>([\\s\\n\\r]*)<L>", "$1");
}
/**
* Returns true if this is a Marine Weather Statement and not a Standalone
* MWS. Standalone MWS do not have VTECs.
*
* @return
*/
protected boolean isMarineProduct() {
VtecObject vtecObj = VtecUtil.parseMessage(text);
boolean marineProduct = vtecObj != null
&& vtecObj.getPhenomena() != null
&& vtecObj.getPhenomena().equals("MA");
return marineProduct;
}
/**
* Tests if the LOCK_START count matches the LOCK_END count.
*
* @return
*/
public static boolean validate(String modified) {
int startCount = StringUtils.countMatches(modified, LOCK_START);
int endCount = StringUtils.countMatches(modified, LOCK_END);
return startCount == endCount;
}
/**
* Tests if the name has been locked already by the template to avoid
* stacked lock tags.
*
* @return
*/
protected boolean hasBeenLocked(String line, String name) {
int index = line.indexOf(name);
if (index != -1) {
int startBefore = line.lastIndexOf(LOCK_START, index);
int endBefore = line.lastIndexOf(LOCK_END, index);
int startAfter = line.indexOf(LOCK_START, index);
int endAfter = line.indexOf(LOCK_END, index);
if (startBefore != -1 && endAfter != -1 && startBefore < endAfter) {
if (startAfter != -1 && startAfter < endAfter
&& startAfter > startBefore) {
return false;
} else if (endBefore != -1 && endBefore > startBefore
&& endBefore < endAfter) {
return false;
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,68 @@
/**
* 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.viz.warngen.text;
/**
* Modifies the warning text by adding 'CORRECTED' in the MND header.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class CorModifyTextBehavior implements IModifyTextBehavior {
private static final String[] types = new String[] { "WARNING", "WATCH",
"STATEMENT", "ADVISORY" };
private static final String correctedText = "...CORRECTED";
@Override
public String modify(String text) {
int index = text.indexOf("NATIONAL WEATHER SERVICE");
int typeIdx = -1, i = 0;
if (index > 0) {
for (i = 0; i < types.length; i++) {
if (text.lastIndexOf(types[i], index) != -1) {
typeIdx = text.lastIndexOf(types[i], index);
break;
}
}
}
if (index > 0 && typeIdx > 0 && !text.contains(correctedText)) {
text = text.substring(0, typeIdx + types[i].length())
+ correctedText
+ text.substring(typeIdx + types[i].length());
}
return text;
}
}

View file

@ -0,0 +1,199 @@
/**
* 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.viz.warngen.text;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.raytheon.viz.warngen.gis.AffectedAreas;
/**
* Locks text patterns for follow up warnings.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class FollowUpLockingBehavior extends AbstractLockingBehavior {
/**
* Locks the appropriate text of the body of an initial warning.
*/
@Override
public void body() {
headlines();
}
/**
* Locks the cancel and the follow up headlines.
*
*/
private void headlines() {
// LOCK_END should not be found at the beginning since the previous line
// should be blank.
Pattern headlinePtrn = Pattern
.compile(
"^\\.\\.\\.(A|THE) (.*) (WARNING|ADVISORY) .*(REMAINS|EXPIRE|CANCELLED).*(\\.\\.\\.)$",
Pattern.MULTILINE);
Matcher m = headlinePtrn.matcher(text);
while (m.find()) {
String originalHeadline = m.group();
String headline = headline(originalHeadline);
headline = remainsInEffect(headline);
headline = expired(headline);
headline = canceled(headline);
this.text = text.replace(originalHeadline, headline);
}
}
/**
* Locks the expired text.
*/
private String expired(String followupHeadline) {
// Based on the templates, the expired text is for the affected areas
// not the canceled areas.
String expire = "(HAS BEEN ALLOWED TO EXPIRE)|(WILL BE ALLOWED TO EXPIRE)|(WILL EXPIRE)|(HAS EXPIRED)|EXPIRED";
String time = "AT \\d{3,4} (AM|PM) \\w{3,4}";
return followupHeadline.replaceAll(expire, REPLACEMENT).replaceAll(
time, REPLACEMENT);
}
/**
* Locks remains in effect text.
*
* @param followupHeadline
* @return
*/
private String remainsInEffect(String followupHeadline) {
return followupHeadline.replaceFirst(
"REMAINS IN EFFECT UNTIL \\d{3,4} (AM|PM) \\w{3,4}", LOCK_START
+ "$0" + LOCK_END);
}
/**
* Locks the canceled text.
*
* @param canceledHeadline
* @return
*/
private String canceled(String canceledHeadline) {
return canceledHeadline.replaceAll("(IS|(HAS BEEN)) CANCELLED",
REPLACEMENT);
}
/**
* Helper method to lock area names and notations in the headline.
*
* @param headline
* @param areas
* @return
*/
private String headline(String headline) {
Set<String> notations = new HashSet<String>();
Set<String> names = new HashSet<String>();
for (AffectedAreas affectedArea : affectedAreas) {
if (affectedArea.getAreaNotation() != null
&& affectedArea.getAreaNotation().trim().length() != 0) {
notations.add(affectedArea.getAreaNotation().toUpperCase());
}
if (affectedArea.getAreasNotation() != null
&& affectedArea.getAreasNotation().trim().length() != 0) {
notations.add(affectedArea.getAreasNotation().toUpperCase());
}
if (affectedArea.getName() != null
&& affectedArea.getName().trim().length() != 0) {
names.add(affectedArea.getName().toUpperCase());
}
}
// Marine products follow different locking rules
if (!isMarineProduct()) {
headline = keywords(headline);
Iterator<String> iterator1 = notations.iterator();
while (iterator1.hasNext()) {
String notation = iterator1.next();
if (!hasBeenLocked(headline, notation)) {
headline = headline.replace(notation, LOCK_START + notation
+ LOCK_END);
}
}
Iterator<String> iterator2 = names.iterator();
while (iterator2.hasNext()) {
String name = iterator2.next();
if (!hasBeenLocked(headline, name)) {
headline = headline.replace(name, LOCK_START + name
+ LOCK_END);
}
}
} else {
// The full headline of a marine product should be locked.
headline = LOCK_START + headline + LOCK_END;
}
return headline;
}
/**
* Common key words to be locked in a headline.
*
* @param headline
* @return
*/
private String keywords(String headline) {
// Locking the start of a head line (...)
headline = headline.replaceFirst("^\\.\\.\\.", LOCK_START + "$0"
+ LOCK_END);
// Locking the end of a head line (...)
if (headline.endsWith("...")) {
headline = headline.substring(0, headline.length() - 3)
+ LOCK_START + "..." + LOCK_END;
}
// Locks warning type (i.e. SEVERE THUNDERSTORM)
headline = headline.replaceAll("(A|THE) (" + warningType + ")",
LOCK_START + "$0" + LOCK_END);
// Locks the 'FOR' in the headline
headline = headline.replaceFirst(" FOR ", " " + LOCK_START + "FOR"
+ LOCK_END + " ");
// Locks the 'AND' in the headline
headline = headline.replaceFirst(" AND ", " " + LOCK_START + "AND"
+ LOCK_END + " ");
return headline;
}
}

View file

@ -0,0 +1,73 @@
/**
* 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.viz.warngen.text;
import java.util.regex.Pattern;
/**
* Common patterns.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public interface ICommonPatterns {
/** Start tag for locking */
public static final String LOCK_START = "<L>";
/** End tag for locking */
public static final String LOCK_END = "</L>";
public static final String newline = "\\n";
// LOCK_END should not be found at the beginning since the previous line
// should be blank.
public static final String ugc = "(^(\\w{2}[CZ]\\d{3}\\S*-\\d{6}-)$|((\\d{3}-)*\\d{6}-)$|((\\d{3}-)+))";
// LOCK_END can be added at the start of the line if a previous line has
// been locked.
public static Pattern listOfAreaNamePtrn = Pattern.compile("^((" + LOCK_END
+ "){0,1}(((\\w+\\s{1})+\\w{2}-)*((\\w+\\s{1})+\\w{2}-))" + newline
+ ")", Pattern.MULTILINE);
// LOCK_END should not be found at the beginning of a first bullet since the
// previous line should be blank.
public static final String firstBullet = "^(\\* (.*) (WARNING|ADVISORY)( FOR(.*)|\\.\\.\\.)"
+ newline + ")";
// LOCK_END can be added at the start of the line if a previous line has
// been locked.
public static Pattern datePtrn = Pattern
.compile(
"^(("
+ LOCK_END
+ "){0,1}\\d{3,4} (AM|PM) (\\w{3,4}) \\w{3} (\\w{3})\\s+(\\d{1,2}) (\\d{4})"
+ newline + ")", Pattern.MULTILINE);
}

View file

@ -0,0 +1,40 @@
/**
* 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.viz.warngen.text;
/**
* Modifies the warning text based on a warning action.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15332 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public interface IModifyTextBehavior {
public String modify(String text);
}

View file

@ -0,0 +1,219 @@
/**
* 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.viz.warngen.text;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.raytheon.uf.common.dataplugin.warning.util.FileUtil;
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.viz.warngen.gis.AffectedAreas;
/**
* Locks text patterns on initial or new warning.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class InitialLockingBehavior extends AbstractLockingBehavior {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(InitialLockingBehavior.class);
private static Pattern immediateCausePtrn = null;
/**
* Locks the appropriate text of the body of an initial warning.
*/
@Override
public void body() {
header();
firstBullet();
secondBullet();
}
/**
* Helper method to determine the index of each bullet.
*
* @return
*/
private Integer[] bulletIndices() {
List<Integer> bulletIndices = new ArrayList<Integer>();
int index = text.indexOf("* ");
while (index >= 0) {
bulletIndices.add(index);
index = text.indexOf("* ", index + 2);
}
return bulletIndices.toArray(new Integer[bulletIndices.size()]);
}
/**
* Locks the header before the first bullet.
*/
private void header() {
// LOCK_END should not be found at the beginning since the previous line
// should be blank.
String h = "^((THE NATIONAL WEATHER SERVICE IN .{1,} HAS ISSUED A)"
+ newline + ")$";
Pattern header = Pattern.compile(h, Pattern.MULTILINE);
find(header.matcher(text));
}
/**
* Locks the affected area names in the first bullet, the immediate cause.
*
* @param start
* @param end
*/
private void firstBullet() {
Integer[] bulletIndices = bulletIndices();
// Short term forecasts don't follow normal bullets?
if (bulletIndices.length < 2) {
return;
}
int start = bulletIndices[0];
int end = bulletIndices[1];
if (immediateCausePtrn == null) {
immediateCausePtrn = getImmediateCausesPtrn();
}
String firstBulletText = text.substring(start, end);
// According to the original WarningTextHandler, marine zone names
// should not be locked. For some reason, this differs from followups as
// stated in DR 15110. Need verification from NWS. This is a variance?
if (!isMarineProduct()) {
Matcher m = null;
for (String line : firstBulletText.split("\\n")) {
if (immediateCausePtrn != null) {
// immediate cause
m = immediateCausePtrn.matcher(line);
if (m.find()) {
String i = line.replace(line, LOCK_START + line
+ LOCK_END);
firstBulletText = firstBulletText.replace(line, i);
continue;
}
}
for (AffectedAreas affectedArea : affectedAreas) {
String name = affectedArea.getName();
String areaNotation = affectedArea.getAreaNotation();
String parentRegion = affectedArea.getParentRegion();
if (name != null && name.trim().length() != 0
&& line.contains(name.toUpperCase())) {
name = name.toUpperCase();
String t = line;
if (!hasBeenLocked(line, name)) {
t = t.replace(name, LOCK_START + name + LOCK_END);
}
if (areaNotation != null
&& areaNotation.trim().length() != 0) {
areaNotation = areaNotation.toUpperCase();
if (!hasBeenLocked(line, areaNotation.toUpperCase())) {
t = t.replace(areaNotation, LOCK_START
+ areaNotation + LOCK_END);
}
}
if (parentRegion != null
&& parentRegion.trim().length() != 0) {
parentRegion = parentRegion.toUpperCase();
if (!hasBeenLocked(line, parentRegion)) {
t = t.replace(parentRegion, LOCK_START
+ parentRegion + LOCK_END);
}
}
if (validate(t)) {
firstBulletText = firstBulletText.replace(line, t);
}
break;
}
}
}
}
firstBulletText = firstBulletText.replaceAll(firstBullet, LOCK_START
+ "$0" + LOCK_END);
this.text = text.replace(text.substring(start, end), firstBulletText);
}
/**
* Locks the second bullet.
*/
private void secondBullet() {
// LOCK_END should not be found at the beginning since the previous line
// should be blank.
String secondBullet = "^(\\* UNTIL \\d{3,4} (AM|PM) \\w{3,4}" + newline
+ ")";
Pattern secondBulletPtrn = Pattern.compile(secondBullet,
Pattern.MULTILINE);
find(secondBulletPtrn.matcher(text));
}
/**
* Set the immediateCausePtrn with the info in immediateCause.text.
*/
private static Pattern getImmediateCausesPtrn() {
String filename = "immediateCause.txt";
StringBuffer pattern = new StringBuffer();
try {
String immediateCause = FileUtil.open(filename, "base");
pattern.append("(.*)(A DAM BREAK");
for (String ic : immediateCause.split("\n")) {
String[] parts = ic.split("\\\\");
pattern.append("| " + parts[1].trim());
}
pattern.append(")(.*)");
return Pattern.compile(pattern.toString());
} catch (Exception e) {
statusHandler
.handle(Priority.ERROR,
"Unable to process immediateCause.txt in the base directory",
e);
}
return null;
}
}

View file

@ -0,0 +1,62 @@
/**
* 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.viz.warngen.text;
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.viz.core.exception.VizException;
import com.raytheon.viz.texteditor.util.VtecUtil;
/**
* Updates the VTEC with a new ETN.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class NewModifyTextBehavior implements IModifyTextBehavior {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(NewModifyTextBehavior.class);
@Override
public String modify(String text) {
try {
text = VtecUtil.getVtec(text);
} catch (VizException e) {
statusHandler
.handle(Priority.SIGNIFICANT,
"WarnGen cannot update the ETN. Please verify the VTEC is valid.",
e);
}
return text;
}
}

View file

@ -0,0 +1,137 @@
/**
* 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.viz.warngen.text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.raytheon.viz.warngen.gis.AffectedAreas;
/**
* Handles the raw text message by applying locked text patterns, wrapping, etc.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WarningTextHandler {
private AbstractLockingBehavior lockingBehavior;
private IModifyTextBehavior modifyTextBehavior;
public String handle(String text, AffectedAreas[] affectedAreas,
AffectedAreas[] canceledAreas) {
long t0 = System.currentTimeMillis();
text = text.toUpperCase();
if (modifyTextBehavior != null) {
text = modifyTextBehavior.modify(text);
System.out.println("Modified text...");
}
if (lockingBehavior != null) {
text = lockingBehavior.lock(text, affectedAreas, canceledAreas);
System.out.println("Locked text...");
}
text = WrapUtil.wrap(text);
System.out.println("Wrapped text...");
text = clean(text);
System.out.println("Time to handle the text: "
+ (System.currentTimeMillis() - t0));
return text;
}
/**
* Sets locking behavior
*
* @param lockingbehavior
*/
public void setLockingBehavior(AbstractLockingBehavior lockingBehavior) {
this.lockingBehavior = lockingBehavior;
}
/**
* Sets the modify text behavior.
*
* @param modifyTextBehavior
*/
public void setModifyTextBehavior(IModifyTextBehavior modifyTextBehavior) {
this.modifyTextBehavior = modifyTextBehavior;
}
private String clean(String text) {
text = removeExtraLines(text);
text = removeLockedBlankLines(text);
return text;
}
/**
* Removes blank extra lines.
*
* @param text
* @return
*/
private String removeExtraLines(String text) {
StringBuffer sb = new StringBuffer();
String[] seperatedLines = text.replaceAll("\r", "").trim().split("\n");
boolean blankLine = false;
for (String line : seperatedLines) {
if (line.replace(AbstractLockingBehavior.LOCK_START, "")
.replace(AbstractLockingBehavior.LOCK_END, "").trim()
.length() > 0) {
sb.append(line + "\n");
blankLine = false;
} else if (blankLine == false) {
sb.append(line + "\n");
blankLine = true;
}
}
return sb.toString().trim();
}
/**
* Removes the locked blank lines.
*
* @param text
* @return
*/
private String removeLockedBlankLines(String text) {
Pattern lockedBlankLinesPattern = Pattern.compile("<L>(\\s*+)</L>",
Pattern.MULTILINE);
Matcher matchLockedBlankLines = lockedBlankLinesPattern.matcher(text);
return matchLockedBlankLines.replaceAll("$1");
}
}

View file

@ -0,0 +1,85 @@
/**
* 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.viz.warngen.text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
/**
* Returns a WarningTextHandler with the appropriate behaviors based on the type
* of warning is created.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WarningTextHandlerFactory {
public static WarningTextHandler getHandler(WarningAction action,
String text, boolean autoLock) {
WarningTextHandler handler = new WarningTextHandler();
if (action == WarningAction.NEW) {
handler.setModifyTextBehavior(new NewModifyTextBehavior());
} else if (action == WarningAction.COR) {
handler.setModifyTextBehavior(new CorModifyTextBehavior());
} else {
System.out.println("The text will not be updated based on action.");
handler.setModifyTextBehavior(null);
}
if (!autoLock) {
System.out.println("Config has autoLock set to 'false'");
handler.setLockingBehavior(null);
} else if (isInitialWarning(action, text)) {
handler.setLockingBehavior(new InitialLockingBehavior());
} else {
handler.setLockingBehavior(new FollowUpLockingBehavior());
}
return handler;
}
private static boolean isInitialWarning(WarningAction action, String text) {
if (action == WarningAction.NEW || action == WarningAction.EXT) {
return true;
} else if (action == WarningAction.COR) {
// TODO Need a better solution not to include the text in the
// factory.
Pattern firstBulletPtrn = Pattern
.compile(InitialLockingBehavior.firstBullet);
Matcher m = firstBulletPtrn.matcher(text);
return m.find();
}
return false;
}
}

View file

@ -0,0 +1,179 @@
/**
* 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.viz.warngen.text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Wraps lines in the warning text that exceed the max width.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 24, 2012 15322 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WrapUtil implements ICommonPatterns {
/** Maximum width of a warning */
private static final int MAX_WIDTH = 69;
private static final String INDENT = " ";
private static final String BULLET_START = "* ";
private static final Pattern wrapUgcPtrn = Pattern.compile("(\\S{1,"
+ (MAX_WIDTH - 1) + "}-)");
private static final Pattern wrapListOfNamesPtrn = Pattern.compile("(.{1,"
+ (MAX_WIDTH - 4) + "} \\w{2}-)");
// Locations in 4th bullet or locations paragraph of followup
// ex: ellipsis, spaces, period
private static final Pattern wrapDefaultPtrn = Pattern
.compile("(\\w{1,}\\.\\.\\.)|(AND \\w+\\.\\.\\.)|(\\w+\\.\\.\\.\\s{1,2})|"
+ "("
+ LOCK_START
+ "\\w+"
+ LOCK_END
+ "\\.\\.\\.)|\\S+\\.\\.\\."
+ LOCK_END
+ "|\\S+\\.\\.\\.|"
+ "(\\s*\\S+\\s+)|(.+\\.)|(\\S+$)");
// ugc pattern
private static final Pattern ugcPtrn = Pattern.compile(ugc);
/**
* Wraps the text independent of being locked before or after.
*
* @param text
* @return
*/
public static String wrap(String text) {
StringBuffer sb = new StringBuffer();
boolean inBullet = false;
String addLine = "";
String[] lines = text.split("\n");
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
String unlocked = line.replaceAll(LOCK_START + "|" + LOCK_END, "");
if (unlocked.trim().length() == 0) { // BLANK LINE
inBullet = false;
addLine = line;
} else if (unlocked.length() <= MAX_WIDTH) { // LESS THAN MAX
// Add indenting if the template didn't account for it yet.
if (inBullet && !unlocked.startsWith(INDENT)) {
sb.append(INDENT);
}
if (unlocked.startsWith(BULLET_START)) {
inBullet = true;
}
addLine = line;
} else { // NEEDS TO BE WRAPPED
addLine = wrapLongLines(line, inBullet);
}
sb.append(addLine);
if (i != lines.length - 1) {
sb.append("\n");
}
}
return sb.toString();
}
/**
* Wraps lines longer than the MAX WIDTH
*
* @param line
* @param inBullet
* @return
*/
private static String wrapLongLines(String line, boolean inBullet) {
StringBuffer sb = new StringBuffer(line.length());
String unlockedLine = line.replaceAll(LOCK_START + "|" + LOCK_END, "");
if (unlockedLine.startsWith(BULLET_START)) {
inBullet = true;
}
Pattern p = getPattern(line);
Matcher m = p.matcher(line.trim());
String tmp = inBullet && !unlockedLine.startsWith(BULLET_START) ? INDENT
: "";
while (m.find()) {
String group = m.group();
String unlocked = group.replaceAll(LOCK_START + "|" + LOCK_END, "");
int len = tmp.replaceAll(LOCK_START + "|" + LOCK_END, "").length();
if (len + unlocked.length() > MAX_WIDTH && tmp.trim().length() > 0) {
sb.append(tmp + "\n");
tmp = inBullet ? INDENT : "";
tmp += group;
} else {
tmp += group;
}
}
if (tmp.trim().length() > 0) {
sb.append(tmp);
}
return sb.toString();
}
/**
* Helper method to return matcher object for wrapping text.
*
* @param line
* @return
*/
private static Pattern getPattern(String line) {
Matcher m = ugcPtrn.matcher(line);
// UGC line or FIPS line
if (m.find()) {
return wrapUgcPtrn;
}
m = listOfAreaNamePtrn.matcher(line);
// List of area names.
if (m.find() && !line.startsWith(BULLET_START)) {
return wrapListOfNamesPtrn;
}
return wrapDefaultPtrn;
}
}

View file

@ -1,22 +1,3 @@
/**
* 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.viz.warngen.util;
import java.util.ArrayList;
@ -34,6 +15,7 @@ import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.viz.warngen.gis.AffectedAreas;
import com.raytheon.viz.warngen.gis.GisUtil;
import com.raytheon.viz.warngen.gis.GisUtil.Direction;
import com.raytheon.viz.warngen.text.ICommonPatterns;
/**
* This utility will provide methods for determining what followup products are
@ -64,8 +46,9 @@ public class FollowUpUtil {
* This method checks whether a particular followup should be available
* given a Warning Record, a vtec Action, and a template configuration
*/
public static boolean checkApplicable(String site, WarngenConfiguration config,
AbstractWarningRecord record, WarningAction action) {
public static boolean checkApplicable(String site,
WarngenConfiguration config, AbstractWarningRecord record,
WarningAction action) {
// Current Time
Calendar cal = Calendar.getInstance();
@ -91,14 +74,16 @@ public class FollowUpUtil {
rval = true;
}
}
if (action == WarningAction.COR && checkCorApplicable(site, config, record)) {
if (action == WarningAction.COR
&& checkCorApplicable(site, config, record)) {
rval = true;
}
}
return rval;
}
private static boolean checkCorApplicable(String site, WarngenConfiguration config, AbstractWarningRecord warnRec) {
private static boolean checkCorApplicable(String site,
WarngenConfiguration config, AbstractWarningRecord warnRec) {
boolean allowsCONProduct = false;
boolean allowsCORProduct = false;
for (String s : config.getFollowUps()) {
@ -113,29 +98,31 @@ public class FollowUpUtil {
if (allowsCORProduct == false) {
return false;
}
CurrentWarnings cw = CurrentWarnings.getInstance(site);
List<AbstractWarningRecord> correctableWarnings = cw.getCorrectableWarnings(warnRec);
List<AbstractWarningRecord> correctableWarnings = cw
.getCorrectableWarnings(warnRec);
boolean wasContinued = false;
for (AbstractWarningRecord w : correctableWarnings) {
if (WarningAction.valueOf(w.getAct()) == WarningAction.CON) {
wasContinued = true;
}
}
// Adding a COR option for continuation follow ups
if (correctableWarnings.isEmpty() == false && ((allowsCONProduct && wasContinued)
|| (allowsCONProduct == false && wasContinued == false))) {
if (correctableWarnings.isEmpty() == false
&& ((allowsCONProduct && wasContinued) || (allowsCONProduct == false && wasContinued == false))) {
return true;
}
return false;
}
/**
* Returns the raw message of the record but removes
* the first wmoid and the pil (the first two lines of the warning)
* Returns the raw message of the record but removes the first wmoid and the
* pil (the first two lines of the warning)
*
* @param record
* @return
*/
@ -169,47 +156,49 @@ public class FollowUpUtil {
return originalMessage;
}
/**
* Returns a list of the canceled areas from the original text
*
* @param originalText
* @return
*/
public static ArrayList<AffectedAreas> canceledAreasFromText(String originalText) {
public static ArrayList<AffectedAreas> canceledAreasFromText(
String originalText) {
boolean ugcdone = false;
boolean namedone = false;
boolean insideHeadline = false;
String ugcLine = "";
String namesLine = "";
String headline = "";
Pattern ugcPtrn = Pattern.compile(ICommonPatterns.ugc);
for (String line : originalText.trim().split("\n")) {
if (line.contains("TEST") || line.trim().length() == 0) {
continue;
}
Matcher m = WarningTextHandler.ugcPtrn.matcher(line);
Matcher m = ugcPtrn.matcher(line);
if (!ugcdone && m.find()) {
ugcLine += m.group();
continue;
} else if (ugcLine.length() > 0) {
ugcdone = true;
}
m = WarningTextHandler.listOfAreaNamePtrn.matcher(line);
m = ICommonPatterns.listOfAreaNamePtrn.matcher(line);
if (!namedone && m.find()) {
namesLine += m.group();
continue;
} else if (namesLine.length() > 0) {
namedone = true;
}
if (line.startsWith("...")) {
headline += line.substring(3);
insideHeadline = true;
} else if (insideHeadline) {
if (line.trim().endsWith("...")) {
headline += line.substring(0,line.length() - 3);
headline += line.substring(0, line.length() - 3);
insideHeadline = false;
break;
}
@ -219,9 +208,9 @@ public class FollowUpUtil {
String[] ugcs = ugcLine.split("-");
String[] names = namesLine.split("-");
String[] areas = headline.split("\\.\\.\\.");
ArrayList<AffectedAreas> al = new ArrayList<AffectedAreas>();
String stateAbbreviation = null;
String areaNotation = null;
String areasNotation = null;
@ -232,7 +221,7 @@ public class FollowUpUtil {
AffectedAreas affectedArea = new AffectedAreas();
String ugc = ugcs[i].trim();
if (ugc.length() == 6) {
stateAbbreviation = ugc.substring(0,2);
stateAbbreviation = ugc.substring(0, 2);
if (ugc.charAt(2) == 'Z') {
areaNotation = "ZONE";
areasNotation = "ZONES";
@ -241,17 +230,18 @@ public class FollowUpUtil {
areasNotation = "COUNTIES";
}
}
fips = ugc.substring(ugc.length() - 3);
if (i < names.length) {
name = names[i].substring(0, names[i].length()-3);
name = names[i].substring(0, names[i].length() - 3);
}
if (name != null) {
for (String area : areas) {
if (area.contains(name)) {
EnumSet<Direction> set = EnumSet.noneOf(Direction.class);
EnumSet<Direction> set = EnumSet
.noneOf(Direction.class);
for (Direction direction : Direction.values()) {
if (area.contains(direction.name())) {
set.add(direction);
@ -262,7 +252,7 @@ public class FollowUpUtil {
}
}
}
affectedArea.setFips(fips);
affectedArea.setStateabbr(stateAbbreviation);
affectedArea.setAreaNotation(areaNotation);
@ -274,11 +264,12 @@ public class FollowUpUtil {
return al;
}
public static String getUgcLineCanFromText(String originalText) {
String ugcLine = "";
for (String line : originalText.trim().split("\n")) {
Matcher m = WarningTextHandler.ugcPtrn.matcher(line);
Pattern ugcPtrn = Pattern.compile(ICommonPatterns.ugc);
for (String line : originalText.trim().split("\n")) {
Matcher m = ugcPtrn.matcher(line);
if (m.find()) {
ugcLine += line;
continue;
@ -286,7 +277,7 @@ public class FollowUpUtil {
break;
}
}
return ugcLine;
}

View file

@ -1,926 +0,0 @@
/**
* 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.viz.warngen.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
import com.raytheon.uf.common.dataplugin.warning.util.FileUtil;
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.viz.texteditor.TextWarningConstants;
import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.texteditor.util.VtecUtil;
import com.raytheon.viz.warngen.gis.AffectedAreas;
import com.raytheon.viz.warngen.gis.AffectedAreasComparator;
/**
* Adds lock tags and wraps the text
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 2, 2010 jsanchez Initial creation
* Aug 29, 2011 10719 rferrel applyLocks no longer allows removal of
* required blank lines.
* May 10, 2012 14681 Qinglu Lin Updated regex string for Pattern listOfAreaNamePtrn, etc.
* May 30, 2012 14749 Qinglu Lin Handled CAN in a CANCON specifically.
* Jun 6, 2012 14749 Qinglu Lin Added code to lock "...THE" in "...THE CITY OF", etc.
* (David's concise approach was used. A quicker but
* lengthy code snippet is available) and to resolve issue with
* empty areaNotation and areasNotation which occurs when,
* for example, District of Columbia is followed by a county.
* Jun 19, 2012 15110 Qinglu Lin Fixed issues in CAN of CANCON: a) ST in any words changed to "ST." and
* locked if county name has ST., e.g., ST. JOSEPH;
* b)Locking beyond the first paragraph for areal flood warning followup;
* c)Locking does not work for areal flood advisory followup;
* d)NullointException/locking does not work for special marine warning
* and locking beyond first paragragh.
* Jul 17, 2012 14989 Qinglu Lin Removed locks, <L> and </L>, for county names in pathcast line.
* Sep 11, 2012 15372 Qinglu Lin Adjusted code so that <L> and </L> in the line of "MAINLY RURAL AREAS"
* were removed as well.
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WarningTextHandler {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(WarningTextHandler.class);
/** Maximum width of a warning */
private static final int MAX_WIDTH = 69;
private static final String LOCK_START = TextWarningConstants.BEGIN_ELEMENT_TAG;
private static final String LOCK_END = TextWarningConstants.END_ELEMENT_TAG;
private static final String TEST_MSG1 = "...THIS MESSAGE IS FOR TEST PURPOSES ONLY...";
private static final String TEST_MSG2 = "THIS IS A TEST MESSAGE.";
private static final String TEST_MSG3 = "THIS IS A TEST MESSAGE. DO NOT TAKE ACTION BASED ON THIS MESSAGE.";
/** Pattern for wrapping a long line */
private static final Pattern wrapRE = Pattern
.compile("(\\S\\S&&([^\\.][^\\.][^\\.]){" + (MAX_WIDTH - 3)
+ ",}|.{1," + (MAX_WIDTH - 3) + "})(" + LOCK_START + "|"
+ LOCK_END + "|-|\\s+|(\\.\\.\\.)|$)");
/** Strictly wraps only after hyphens */
private static final Pattern hyphenWrapRE = Pattern.compile("(.{1,"
+ (MAX_WIDTH - 1) + "}-)");
private static final Pattern datePtrn = Pattern
.compile("(\\d{1,2})(\\d{2})\\s(AM|PM)\\s(\\w{3,4})\\s\\w{3}\\s(\\w{3})\\s{1,}(\\d{1,2})\\s(\\d{4})");
/** The UGC line (ex. NEC003-008-010-110325) */
public static final Pattern ugcPtrn = Pattern
.compile("((\\w{2}[CZ](\\d{3}-){1,}){1,})|(\\d{3}-){1,}");
/** The VTEC line (ex. /O.NEW.KOAX.TO.W.0001.110715T1722Z-110715T1730Z) */
private static final Pattern vtecPtrn = Pattern
.compile("/[OTEX]\\.([A-Z]{3})\\.[A-Za-z0-9]{4}\\.[A-Z]{2}\\.[WAYSFON]\\.\\d{4}\\.\\d{6}T\\d{4}Z-\\d{6}T\\d{4}Z/");
/**
* The HTEC line (ex.
* /00000.0.${ic}.000000T0000Z.000000T0000Z.000000T0000Z.OO/)
*/
private static final Pattern htecPtrn = Pattern
.compile("/[A-Za-z0-9]{5}.[0-3NU].\\w{2}.\\d{6}T\\d{4}Z.\\d{6}T\\d{4}Z.\\d{6}T\\d{4}Z.\\w{2}/");
/** ex. * TORNADO WARNING FOR... */
private static final Pattern firstBulletPtrn = Pattern
.compile("\\*\\s(.*)\\s(WARNING|ADVISORY)(\\sFOR(.*)|\\.\\.\\.)");
private static final Pattern cancelPtrn = Pattern
.compile("(|(.*))(IS CANCELLED\\.\\.\\.)");
private static final Pattern cancelOnlyPtrn = Pattern
.compile("(CANCELLED<\\/L>\\.\\.\\.)");
private static final Pattern expirePtrn = Pattern
.compile("(|(.*))((EXPIRED|WILL EXPIRE)\\sAT\\s\\d{3,4}\\s(AM|PM)\\s\\w{3}...)");
private static final Pattern headlinePtrn = Pattern
.compile("(\\.\\.\\.((A|THE)\\s(.*)\\s(WARNING|ADVISORY))\\s(FOR|(REMAINS IN EFFECT (|(UNTIL\\s\\d{3,4}\\s(AM|PM)\\s\\w{3})))))(|(.*))");
private static final Pattern canVtecPtrn = Pattern.compile("(\\.CAN\\.)");
private static final Pattern smwCanPtrn = Pattern.compile("(\\.MA\\.W\\.)");
private static final Pattern afaCanPtrn = Pattern.compile("(\\.FA\\.Y\\.)");
private static final Pattern smwHeadlinePtrn = Pattern
.compile("(THE AFFECTED AREAS WERE)");
private static Pattern immediateCausePtrn = null;
/** ex. SARPY NE-DOUGLAS NE-WASHINGTON NE- */
public static final Pattern listOfAreaNamePtrn = Pattern
.compile("(((\\w{1,}\\s{1}){1,}\\w{2}-){0,}((\\w{1,}\\s{1}){1,}\\w{2}-))");
private static final Pattern secondBulletPtrn = Pattern.compile("\\*(|\\s"
+ TEST_MSG2 + ")\\sUNTIL\\s\\d{3,4}\\s(AM|PM)\\s\\w{3,4}");
private static final Pattern testMessagePtrn = Pattern.compile("(|.*)("
+ TEST_MSG2 + ")(.*)");
private static final Pattern latLonPtrn = Pattern
.compile("^LAT...LON+(\\s\\d{3,4}\\s\\d{3,5}){1,}");
private static final Pattern subLatLonPtrn = Pattern
.compile("^((?!TIME...MOT... LOC))\\s{1,}\\d{3,4}\\s\\d{3,5}(|(\\s\\d{3,4}\\s\\d{3,5}){1,})");
private static final Pattern tmlPtrn = Pattern
.compile("TIME...MOT...LOC \\d{3,4}Z\\s\\d{3}DEG\\s\\d{1,3}KT((\\s\\d{3,4}\\s\\d{3,5}){1,})");
private static final Pattern subTMLPtrn = Pattern
.compile("(\\d{3,5}\\s){1,}");
private static final Pattern lockedBlankLinesPattern = Pattern.compile(
"<L>(\\s*+)</L>", Pattern.MULTILINE);
private static final String LOCK_REPLACEMENT_TEXT = LOCK_START + "$0"
+ LOCK_END;
private static final Pattern extraTokensPattern = Pattern
.compile("\\b(?:THE|IS|CANCELLED)\\b");
static {
String pattern = "";
// Load immediateCausePtrn with immediateCause.txt
try {
String immediateCause = FileUtil.open("immediateCause.txt", "base");
pattern = "(.*)(A DAM BREAK";
for (String ic : immediateCause.split("\n")) {
String[] parts = ic.split("\\\\");
pattern += "| " + parts[1].trim();
}
pattern += ")(.*)";
immediateCausePtrn = Pattern.compile(pattern);
} catch (Exception e) {
statusHandler
.handle(Priority.ERROR,
"Unable to process immediateCause.txt in the base directory",
e);
}
}
public static String handle(String originalMessage, AffectedAreas[] areas,
AffectedAreas[] canceledAreas, WarningAction action,
WarningAction action2, boolean autoLock) {
long t0 = System.currentTimeMillis();
if (action == WarningAction.NEW) {
try {
originalMessage = VtecUtil.getVtec(originalMessage);
} catch (Exception e) {
statusHandler
.handle(Priority.SIGNIFICANT,
"WarnGen cannot update the ETN. Please verify the VTEC is valid.",
e);
}
} else if (action == WarningAction.COR) {
originalMessage = addCorrectedText(originalMessage);
}
originalMessage = wrap(originalMessage);
if (autoLock) {
boolean initialWarning = (action == WarningAction.NEW || action == WarningAction.EXT);
if (action == WarningAction.COR) {
Matcher m = firstBulletPtrn.matcher(originalMessage);
initialWarning = m.find();
}
List<AffectedAreas> areasArr = areas != null ? Arrays.asList(areas)
: null;
List<AffectedAreas> canceledAreasArr = canceledAreas != null ? Arrays
.asList(canceledAreas) : null;
originalMessage = applyLocks(originalMessage, areasArr,
canceledAreasArr, initialWarning, action2);
}
originalMessage = removeExtraLines(originalMessage);
// remove locked blank lines
Matcher matchLockedBlankLines = lockedBlankLinesPattern
.matcher(originalMessage);
originalMessage = matchLockedBlankLines.replaceAll("$1");
System.out.println("Time to handle the text: "
+ (System.currentTimeMillis() - t0));
return originalMessage;
}
private static String addCorrectedText(String originalMessage) {
int index = originalMessage.indexOf("NATIONAL WEATHER SERVICE");
String[] types = new String[] { "WARNING", "WATCH", "STATEMENT",
"ADVISORY" };
int typeIdx = -1, i = 0;
if (index > 0) {
for (i = 0; i < types.length; i++) {
if (originalMessage.lastIndexOf(types[i], index) != -1) {
typeIdx = originalMessage.lastIndexOf(types[i], index);
break;
}
}
}
if (index > 0 && typeIdx > 0
&& !originalMessage.contains("...CORRECTED")) {
originalMessage = originalMessage.substring(0,
typeIdx + types[i].length())
+ "...CORRECTED"
+ originalMessage.substring(typeIdx + types[i].length());
}
return originalMessage;
}
private static String applyLocks(String originalMessage,
List<AffectedAreas> areas, List<AffectedAreas> canceledAreas,
boolean initialWarning, WarningAction action) {
boolean firstBulletFound = false;
boolean insideFirstBullet = false;
boolean secondBulletFound = false;
boolean headlineFound = false;
// for CAN in a CANCON
boolean cancelVtecLineFound = false;
boolean insideLatLon = false;
boolean insideTML = false;
boolean checkForMND = true;
StringBuffer sb = new StringBuffer();
String[] seperatedLines = originalMessage.trim().split("\n");
Matcher m = null;
VtecObject vtecObj = VtecUtil.parseMessage(originalMessage);
boolean marineProduct = vtecObj != null
&& vtecObj.getPhenomena() != null
&& vtecObj.getPhenomena().equals("MA");
AffectedAreasComparator comparator = new AffectedAreasComparator(
new ArrayList<String>());
if (areas != null) {
Collections.sort(areas, comparator);
}
if (canceledAreas != null) {
Collections.sort(canceledAreas, comparator);
}
boolean startLines = true;
// Set before to false if the line is beyond
// "THE NATIONAL WEATHER SERVICE IN" line.
boolean before = true;
boolean isCancelledFound = false;
// for CAN of CANCON of Special Marine Warning
boolean smwCan = false;
boolean lockSmwCan = false;
// for CAN of CANCON of Areal Flood Advisory
boolean afaCan = false;
int afaCanBlankline = 0;
String[] tokens = null;
if (canceledAreas != null) {
String areaNames = "";
for (AffectedAreas area : canceledAreas) {
String areaName = area.getName();
if (areaName != null && areaName.length() > 0) {
areaName = areaName.toUpperCase();
areaNames += areaName + " ";
}
}
areaNames = areaNames.trim();
HashSet<String> areaNameSet = new HashSet<String>();
String[] tokens0 = areaNames.split(" ");
int tokensLength = tokens0.length;
for (int i = 0; i < tokensLength; i++) {
areaNameSet.add(tokens0[i]);
}
tokens = (String[]) areaNameSet.toArray(new String[areaNameSet
.size()]);
String s0;
for (int i = 0; i < areaNameSet.size() - 1; i++) {
s0 = tokens[i];
String s1;
for (int j = i; j < areaNameSet.size(); j++) {
if (tokens[j].length() > s0.length()) {
s1 = s0;
s0 = tokens[j];
tokens[j] = s1;
}
}
tokens[i] = s0;
}
}
boolean ruralFound = false, ruralReplaced = false;
ArrayList<String> usedAreaNotations = new ArrayList<String>();
for (int lineIndex = 0; lineIndex < seperatedLines.length; ++lineIndex) {
String line = seperatedLines[lineIndex];
if (line.contains("THE NATIONAL WEATHER SERVICE IN")
|| line.contains("OTHER LOCATIONS IMPACTED")) {
before = false;
}
if (!ruralFound && !ruralReplaced && line.contains("MAINLY RURAL AREAS")) {
ruralFound = true;
}
// This prevents blank line(s) after the header from being locked.
if (startLines && lineIndex > 1) {
startLines = line.trim().length() == 0;
}
try {
// MND header
if (checkForMND
&& (line.contains("EAS ACTIVATION")
|| line.contains("IMMEDIATE BROADCAST")
|| line.startsWith("NATIONAL WEATHER SERVICE")
|| line.startsWith("THE NATIONAL WEATHER SERVICE")
|| line.startsWith("ISSUED BY ")
|| line.contains("...CORRECTED") || ((line
.endsWith("WARNING")
|| line.endsWith("WATCH")
|| line.endsWith("STATEMENT") || line
.endsWith("ADVISORY")) && line.startsWith("*") == false))) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
}
// Locking the UGC line
m = ugcPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
}
// Locking the VTEC
m = vtecPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
// check out if .CAN. is in VTEC line of a CANCON product.
m = canVtecPtrn.matcher(line);
if (action == WarningAction.CANCON && m.find()) {
cancelVtecLineFound = true;
m = smwCanPtrn.matcher(line);
if (m.find())
smwCan = true;
m = afaCanPtrn.matcher(line);
if (m.find())
afaCan = true;
}
continue;
}
// Locking the HTEC
m = htecPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + m.group(0) + "\n" + LOCK_END);
continue;
}
if (before) {
m = listOfAreaNamePtrn.matcher(line);
if (m.matches()) {
if (!(line.contains("!**") || line.contains("**!") || line
.contains("OTHER LOCATIONS"))) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
}
}
}
// Locking Date in the MND header
m = datePtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
}
if (ruralFound)
if (line.trim().length() == 0)
ruralFound = false;
else {
line = line.replace("<L>", "");
line = line.replace("</L>", "");
sb.append(line + "\n");
if (!ruralReplaced)
ruralReplaced = true;
continue;
}
if (line.trim().length() == 0) {
insideTML = false;
insideLatLon = false;
headlineFound = false;
if (smwCan) {
if (lockSmwCan)
cancelVtecLineFound = false;
lockSmwCan = false;
}
if (afaCan) {
afaCanBlankline += 1;
if (afaCanBlankline > 1) {
afaCan = false;
cancelVtecLineFound = false;
}
}
if (startLines) {
// Don't lock blank line after header
sb.append("\n");
} else if (sb.lastIndexOf("\n") == (sb.length() - 1)) {
// Put lock at end of previous line to prevent removal
// of leading blank line.
sb.setLength(sb.length() - 1);
sb.append(LOCK_START + "\n\n" + LOCK_END);
} else {
sb.append(LOCK_START + "\n" + LOCK_END);
}
continue;
}
// Hack!
// This is needed due to a drifting <L>
// due tothe replaceAll("</L>([\\s\\n\\r]*)<L>", "$1");
if (line.startsWith("* AT") || line.startsWith("AT")) {
sb.append(LOCK_START + "" + LOCK_END);
headlineFound = false;
}
if (initialWarning) {
// Locking first bullet
m = firstBulletPtrn.matcher(line);
if (m.find() && firstBulletFound == false) {
checkForMND = false;
sb.append(LOCK_START + line + "\n" + LOCK_END);
firstBulletFound = true;
insideFirstBullet = true;
continue;
}
if (insideFirstBullet) {
// Removes extra spaces between texts in the first
// bullet
line = line.replaceAll("\\s{3}", " ").replaceAll(
"\\b\\s{2,}\\b", " ");
// Lock immediate cause (hydro) (DR 10329)
if (immediateCausePtrn != null) {
m = immediateCausePtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue; // immediate cause on its own line
}
}
boolean countyFound = false;
boolean areaNotationFound = false;
boolean stateFound = false;
if (areas != null && !marineProduct) {
for (int i = areas.size() - 1; i >= 0; i--) {
AffectedAreas area = areas.get(i);
// Lock counties & independent cities (DR 10331)
if (!countyFound
&& area.getName() != null
&& area.getName().length() > 0
&& line.contains(area.getName()
.toUpperCase())) {
line = line.replaceFirst(area.getName()
.toUpperCase(), LOCK_START
+ area.getName().toUpperCase()
+ LOCK_END);
countyFound = true;
}
if (!areaNotationFound
&& area.getAreaNotation() != null
&& area.getAreaNotation().length() > 0
&& line.contains(area.getAreaNotation()
.toUpperCase())) {
line = line.replaceFirst(
" " + area.getAreaNotation(),
LOCK_START + " "
+ area.getAreaNotation()
+ LOCK_END);
areaNotationFound = true;
}
// Lock States (DR 10325)
if (!stateFound
&& area.getParentRegion() != null
&& area.getParentRegion().length() > 0
&& line.contains(area.getParentRegion()
.toUpperCase())) {
line = line.replaceFirst(area
.getParentRegion().toUpperCase(),
LOCK_START
+ area.getParentRegion()
.toUpperCase()
+ LOCK_END);
stateFound = true;
}
}
}
}
// Locking second bullet
m = secondBulletPtrn.matcher(line);
if (m.find() && secondBulletFound == false) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
insideFirstBullet = false;
secondBulletFound = true;
continue;
}
} else {
usedAreaNotations.clear();
// head line pattern
m = headlinePtrn.matcher(line);
if (m.find()) {
checkForMND = false;
headlineFound = true;
line = line.replace(m.group(2), LOCK_START + m.group(2)
+ LOCK_END);
}
// CAN portion in a CANCON
if (cancelVtecLineFound) {
if (smwCan) {
// lock marine zone names
if (lockSmwCan) {
if (line.length() == 0) {
lockSmwCan = false;
} else
line = LOCK_START + line + LOCK_END;
} else {
m = smwHeadlinePtrn.matcher(line);
if (m.find())
lockSmwCan = true;
}
} else {
for (String s : tokens)
if (line.contains(s))
line = line.replace(s, LOCK_START + s
+ LOCK_END);
for (AffectedAreas area : canceledAreas) {
String areaNotation = area.getAreaNotation();
if (areaNotation != null) {
// areaNotation, e.g., COUNTY
if (areaNotation != null
&& areaNotation.length() > 0
&& !usedAreaNotations
.contains(areaNotation)
&& line.contains(areaNotation)) {
line = line.replaceAll(areaNotation,
LOCK_START + areaNotation
+ LOCK_END);
usedAreaNotations.add(areaNotation);
}
// areasNotation, e.g., COUNTIES
String areasNotation = area
.getAreasNotation().toUpperCase();
if (areasNotation != null
&& areasNotation.length() > 0
&& !usedAreaNotations
.contains(areasNotation)
&& line.contains(areasNotation)) {
line = line.replaceAll(areasNotation,
LOCK_START + areasNotation
+ LOCK_END);
usedAreaNotations.add(areasNotation);
}
}
}
// locking "THE" in "THE CITY OF MANASSAS", "...THE"
// in "...THE CITY",
// and "IS" or "CANCELLED" in "IS CANCELLED...".
line = extraTokensPattern.matcher(line).replaceAll(
LOCK_REPLACEMENT_TEXT);
}
if (cancelVtecLineFound && isCancelledFound)
cancelVtecLineFound = false;
m = cancelOnlyPtrn.matcher(line);
if (m.find())
cancelVtecLineFound = false;
sb.append(line + "\n");
continue;
} else {
// follow-ups other than CAN in a CANCON
if (headlineFound) {
usedAreaNotations.clear();
if (areas != null && !marineProduct) {
for (AffectedAreas area : areas) {
if (area.getName() != null
&& line.contains(area.getName()
.toUpperCase())) {
line = line.replaceFirst(area.getName()
.toUpperCase(), LOCK_START
+ area.getName().toUpperCase()
+ LOCK_END);
}
if (area.getAreaNotation() != null
&& !usedAreaNotations.contains(area
.getAreaNotation()
.toUpperCase())
&& line.contains(area
.getAreaNotation())) {
line = line
.replaceAll(
" "
+ area.getAreaNotation()
.toUpperCase(),
LOCK_START
+ " "
+ area.getAreaNotation()
+ LOCK_END);
usedAreaNotations.add(area
.getAreaNotation()
.toUpperCase());
}
}
}
m = cancelPtrn.matcher(line);
if (m.find()) {
line = line.replaceFirst(m.group(3), LOCK_START
+ m.group(3) + LOCK_END);
if (canceledAreas != null) {
for (AffectedAreas canceledArea : canceledAreas) {
if (line.contains(canceledArea
.getName().toUpperCase())) {
line = line
.replaceFirst(
canceledArea
.getName()
.toUpperCase(),
LOCK_START
+ canceledArea
.getName()
.toUpperCase()
+ LOCK_END);
}
}
}
headlineFound = false;
}
m = expirePtrn.matcher(line);
if (m.find()) {
line = line.replaceFirst(m.group(3), LOCK_START
+ m.group(3) + LOCK_END);
headlineFound = false;
}
sb.append(line + "\n");
continue;
}
}
}
// Locking LAT...LON
m = latLonPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
insideLatLon = true;
continue;
}
if (insideLatLon) {
m = subLatLonPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
} else {
insideLatLon = false;
}
}
// PRECAUTIONARY/PREPAREDNESS ACTIONS, $$, &&
if (line.equals("PRECAUTIONARY/PREPAREDNESS ACTIONS...")
|| line.startsWith("$$") || line.startsWith("&&")) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
}
// Locking TIME...MOT..LOC
m = tmlPtrn.matcher(line);
if (m.find()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
insideTML = true;
continue;
}
if (insideTML) {
m = subTMLPtrn.matcher(line);
if (m.matches()) {
sb.append(LOCK_START + line + "\n" + LOCK_END);
continue;
} else {
insideTML = false;
}
}
// Test lines
if (line.equals(TEST_MSG3)
|| line.equals(TEST_MSG1)
|| (line.startsWith("TEST...") && line
.endsWith("...TEST"))) {
sb.append(LOCK_START + line + LOCK_END + "\n");
continue;
}
m = testMessagePtrn.matcher(line);
if (m.find()) {
line = line.replace(m.group(2), LOCK_START + m.group(2)
+ LOCK_END);
}
} catch (Exception e) {
// If an exception is thrown,
// log the exception but continue locking text
statusHandler.handle(Priority.PROBLEM, "Error locking line: "
+ line + "\n", e);
}
sb.append(line + "\n");
insideLatLon = false;
insideTML = false;
}
String rval = sb.toString().trim();
// where a lock close and lock open are only separated by whitespace
// remove the close and open to join the two locked areas
rval = rval.replaceAll("</L>([\\s\\n\\r]*)<L>", "$1");
return rval;
}
private static String wrap(String str) {
StringBuffer sb = new StringBuffer();
boolean inLocations = false;
boolean first = true;
boolean inBullet = false;
String[] values = str.split("\n");
for (String v : values) {
String ignoreLockTags = v.replace(LOCK_START, "").replace(LOCK_END,
"");
if (ignoreLockTags.length() <= MAX_WIDTH) {
if (!first) {
sb.append("\n");
if (inBullet && !ignoreLockTags.startsWith(" ")) {
sb.append(" ");
}
}
if (ignoreLockTags.startsWith("* ")) {
inBullet = true;
if (ignoreLockTags.startsWith("* LOCATIONS")) {
inLocations = true;
}
} else if (ignoreLockTags.trim().equals("")) {
inBullet = false;
inLocations = false;
}
sb.append(v);
} else {
Matcher m = null;
if (ignoreLockTags.startsWith("* ")) {
inBullet = true;
if (ignoreLockTags.startsWith("* LOCATIONS")) {
inLocations = true;
}
} else if (ignoreLockTags.startsWith("...")
&& ignoreLockTags.endsWith("...")) {
// headline found
// use line without lock tags because, lock tags adds to
// character count with doing a regex pattern
v = ignoreLockTags;
}
if (inLocations) {
sb.append("\n");
sb.append(wrapLocations(v));
continue;
} else {
m = ugcPtrn.matcher(v);
if (m.find()) {
m = hyphenWrapRE.matcher(v);
} else {
m = listOfAreaNamePtrn.matcher(v);
if (m.matches()) {
m = hyphenWrapRE.matcher(v);
} else {
m = wrapRE.matcher(v);
}
}
}
while (m.find()) {
String group = m.group().trim();
if (group.trim().length() == 0) {
break;
}
sb.append("\n");
if (group.startsWith("* ") == false && inBullet) {
sb.append(" ");
}
sb.append(group);
}
}
first = false;
}
return sb.toString();
}
private static String wrapLocations(String locationsLine) {
StringBuffer sb = new StringBuffer();
String line = " ";
String[] locations = locationsLine.split("\\.\\.\\.");
for (int i = 0; i < locations.length; i++) {
String location = locations[i];
int size = (i == locations.length - 1) ? location.length()
: location.length() + 3;
if (line.length() + size >= MAX_WIDTH - 2) {
sb.append(line + "\n");
line = " ";
}
if (i == locations.length - 1) {
line += location;
} else {
line += location + "...";
}
}
sb.append(line + "\n");
return sb.toString();
}
private static String removeExtraLines(String originalMessage) {
StringBuffer sb = new StringBuffer();
String[] seperatedLines = originalMessage.replaceAll("\r", "").trim()
.split("\n");
boolean blankLine = false;
for (String line : seperatedLines) {
if (line.replace(LOCK_START, "").replace(LOCK_END, "").trim()
.length() > 0) {
sb.append(line + "\n");
blankLine = false;
} else if (blankLine == false) {
sb.append(line + "\n");
blankLine = true;
}
}
return sb.toString().trim();
}
}