/* * Copyright 2013-2015 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttv.verifier.util; import java.math.BigInteger; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.xml.sax.Locator; import com.skynav.ttv.util.Location; import com.skynav.ttv.util.Reporter; import com.skynav.ttv.verifier.VerifierContext; import com.skynav.ttv.verifier.util.NegativeTreatment; import com.skynav.ttv.verifier.util.ZeroTreatment; public class Integers { private static final Pattern integerPattern = Pattern.compile("([\\+\\-]?)(\\d+)"); private static boolean isInteger(String value, Location location, VerifierContext context, Object[] treatments, Integer[] outputInteger) { Reporter reporter = (context != null) ? context.getReporter() : null; Locator locator = location.getLocator(); Matcher m = integerPattern.matcher(value); if (m.matches()) { String number = m.group(0); if (number.charAt(0) == '+') number = number.substring(1); int numberValue; try { numberValue = new BigInteger(number).intValue(); } catch (NumberFormatException e) { return false; } if (treatments != null) { if (numberValue < 0) { NegativeTreatment negativeTreatment = (NegativeTreatment) treatments[0]; if (negativeTreatment == NegativeTreatment.Error) return false; else if (reporter != null) { if (negativeTreatment == NegativeTreatment.Warning) { if (reporter.logWarning(reporter.message(locator, "*KEY*", "Negative <integer> expression {0} should not be used.", Numbers.normalize(numberValue)))) { treatments[0] = NegativeTreatment.Allow; // suppress second warning return false; } } else if (negativeTreatment == NegativeTreatment.Info) { reporter.logInfo(reporter.message(locator, "*KEY*", "Negative <integer> expression {0} used.", Numbers.normalize(numberValue))); } } } else if (numberValue == 0) { assert treatments.length > 1; ZeroTreatment zeroTreatment = (ZeroTreatment) treatments[1]; if (zeroTreatment == ZeroTreatment.Error) return false; else if (reporter != null) { if (zeroTreatment == ZeroTreatment.Warning) { if (reporter.logWarning(reporter.message(locator, "*KEY*", "Zero <integer> expression {0} should not be used.", Numbers.normalize(numberValue)))) { treatments[1] = ZeroTreatment.Allow; // suppress second warning return false; } } else if (zeroTreatment == ZeroTreatment.Info) { reporter.logInfo(reporter.message(locator, "*KEY*", "Zero <integer> expression {0} used.", Numbers.normalize(numberValue))); } } } } if (outputInteger != null) outputInteger[0] = Integer.valueOf(numberValue); return true; } else return false; } private static void badInteger(String value, Location location, VerifierContext context, Object[] treatments) { Reporter reporter = context.getReporter(); Locator locator = location.getLocator(); boolean negative = false; int numberValue = 0; do { int valueIndex = 0; int valueLength = value.length(); BigInteger integralPart = null; char c; // whitespace before optional sign if (valueIndex == valueLength) break; c = value.charAt(valueIndex); if (Characters.isXMLSpace(c)) { while (Characters.isXMLSpace(c)) { if (++valueIndex >= valueLength) break; c = value.charAt(valueIndex); } reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, XML space padding not permitted before integer.")); } // optional sign before non-negative-integer if (valueIndex == valueLength) break; c = value.charAt(valueIndex); if (c == '+') ++valueIndex; else if (c == '-') { negative = true; ++valueIndex; } // whitespace before non-negative-number if (valueIndex == valueLength) break; c = value.charAt(valueIndex); if (Characters.isXMLSpace(c)) { while (Characters.isXMLSpace(c)) { if (++valueIndex >= valueLength) break; c = value.charAt(valueIndex); } reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, XML space padding not permitted between sign and non-negative-integer.")); } // non-negative-number (integral part only) if (valueIndex == valueLength) break; c = value.charAt(valueIndex); if (Characters.isDigit(c)) { StringBuffer sb = new StringBuffer(); while (Characters.isDigit(c)) { sb.append(c); if (++valueIndex >= valueLength) break; c = value.charAt(valueIndex); } if (sb.length() > 0) { try { integralPart = new BigInteger(sb.toString()); } catch (NumberFormatException e) { } } } // non-negative-number if (integralPart == null) { reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, missing non-negative integer after optional sign.")); } else { numberValue = integralPart.intValue(); } // whitespace after number if (valueIndex == valueLength) break; c = value.charAt(valueIndex); if (Characters.isXMLSpace(c)) { while (Characters.isXMLSpace(c)) { if (++valueIndex >= valueLength) break; c = value.charAt(valueIndex); } reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, XML space padding not permitted after integer.")); } // garbage after (number S*) if (valueIndex < valueLength) { StringBuffer sb = new StringBuffer(); while (valueIndex < valueLength) { sb.append(value.charAt(valueIndex++)); } reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, unrecognized characters not permitted after integer, got ''{0}''.", sb.toString())); } } while (false); if (negative) numberValue = -numberValue; if (treatments != null) { if ((numberValue < 0) && (((NegativeTreatment)treatments[0]) == NegativeTreatment.Error)) { reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, negative value {0} not permitted.", Numbers.normalize(numberValue))); } else if ((numberValue == 0) && (((ZeroTreatment)treatments[1]) == ZeroTreatment.Error)) { reporter.logInfo(reporter.message(locator, "*KEY*", "Bad <integer> expression, zero value not permitted.")); } } } public static boolean isIntegers(String value, Location location, VerifierContext context, Integer[] minMax, Object[] treatments, List<Integer> outputIntegers) { List<Integer> integers = new java.util.ArrayList<Integer>(); String [] integerComponents = value.split("[ \t\r\n]+"); int numComponents = integerComponents.length; for (String component : integerComponents) { Integer[] integer = new Integer[1]; if (isInteger(component, location, context, treatments, integer)) integers.add(integer[0]); else return false; } if (numComponents < minMax[0]) return false; else if (numComponents > minMax[1]) return false; if (outputIntegers != null) { outputIntegers.clear(); outputIntegers.addAll(integers); } return true; } public static void badIntegers(String value, Location location, VerifierContext context, Integer[] minMax, Object[] treatments) { Reporter reporter = context.getReporter(); Locator locator = location.getLocator(); String [] integerComponents = value.split("[ \t\r\n]+"); int numComponents = integerComponents.length; Object[] treatmentsInner = (treatments != null) ? new Object[] { treatments[0], treatments[1] } : null; for (String component : integerComponents) { if (!isInteger(component, location, context, treatmentsInner, null)) badInteger(component, location, context, treatmentsInner); } if (minMax != null) { if (numComponents < minMax[0]) { reporter.logInfo(reporter.message(locator, "*KEY*", "Missing <integer> expression, got {0}, but expected at least {1} <integer> expressions.", numComponents, minMax[0])); } else if (numComponents > minMax[1]) { reporter.logInfo(reporter.message(locator, "*KEY*", "Extra <integer> expression, got {0}, but expected no more than {1} <integer> expressions.", numComponents, minMax[1])); } } } }