/* * Copyright 2016 Skynav, Inc. All rights reserved. * Portions Copyright 2009 Extensible Formatting Systems, Inc (XFSI). * * 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.xfsi.xav.validation.validator; import java.io.IOException; import java.io.InputStream; import java.io.StreamTokenizer; import java.io.StringReader; import java.net.URL; import java.text.MessageFormat; import java.util.Enumeration; import java.util.InvalidPropertiesFormatException; import java.util.Properties; import com.xfsi.xav.test.Test; import com.xfsi.xav.test.TestInfo; import com.xfsi.xav.test.TestManager; import com.xfsi.xav.util.Error; import com.xfsi.xav.util.Error.Category; import com.xfsi.xav.util.Error.ContentType; import com.xfsi.xav.util.Error.Reference; import com.xfsi.xav.util.Error.TestType; import com.xfsi.xav.util.Progress; import com.xfsi.xav.util.property.PropertyMessageKey; import com.xfsi.xav.util.property.PropertyMessageKeyTokenizer; public abstract class AbstractValidator implements Test { private static final String PROPERTY_FILE_NAME = "properties.xml"; private static final String MSG_PREFIX = "msg."; private static final String INTERNAL_ERROR_ID = "XAV_INTERNAL: "; private static final String TKN01F001 = "malformed message key "; private static final String TKN01F002 = "null message key "; protected Properties properties; protected boolean propertiesInitialized; protected String propertyFile; private boolean valid; private String lastError; private PropertyMessageKeyTokenizer keyTokenizer; public AbstractValidator() { this.propertyFile = PROPERTY_FILE_NAME; } public AbstractValidator(String propertyFile) { this.propertyFile = propertyFile; } public boolean isRunnable(TestManager tm, TestInfo ti) { return true; } public boolean isValid() { return valid; } public String getLastError() { return lastError; } protected void initState(TestManager tm, TestInfo ti) throws Exception { loadProperties(); } private void loadProperties() throws Exception { if (!propertiesInitialized) { InputStream is = null; valid = false; lastError = null; propertiesInitialized = true; try { URL url = getClass().getResource(propertyFile); if (url != null) { if ((is = url.openStream()) != null) { valid = true; properties = new Properties(); properties.loadFromXML(is); try { Enumeration<?> e = properties.propertyNames(); for (; e.hasMoreElements();) { String propName = (String) e.nextElement(); if (propName.startsWith(MSG_PREFIX)) getMsgKey(propName); } } catch (PropertyMessageKey.MalformedKeyException e) { valid = false; lastError = e.toString(); throw e; } } } } catch (InvalidPropertiesFormatException e) { valid = false; lastError = e.toString(); throw e; } catch (IOException e) { valid = false; lastError = e.toString(); throw e; } finally { closeSafely(is); } } } private void closeSafely(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { } } } private PropertyMessageKey getMsgKey(String key) throws PropertyMessageKey.MalformedKeyException { if (key != null) { if (key.startsWith(MSG_PREFIX)) return parseKey(key.substring(MSG_PREFIX.length())); throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", invalid prefix, msg. expected"); } throw new PropertyMessageKey.MalformedKeyException(TKN01F002); } protected String msg(String key, Object... formatArguments) { if (properties != null) { try { parseKey(key); String msg = properties.getProperty(MSG_PREFIX + key); if (msg != null && msg.length() > 0) return MessageFormat.format(msg, formatArguments); } catch (PropertyMessageKey.MalformedKeyException e) { return INTERNAL_ERROR_ID + e.toString(); } } return INTERNAL_ERROR_ID + "missing message: " + key; } protected String msgFormatterNV(String key, Object... formatArguments) { if (properties != null) { String msg = properties.getProperty(MSG_PREFIX + key); if (msg != null && msg.length() > 0) return String.format(msg, formatArguments); } return INTERNAL_ERROR_ID + "missing message: " + key; } protected PropertyMessageKey parseKey(String key) throws PropertyMessageKey.MalformedKeyException { if (keyTokenizer == null) keyTokenizer = new PropertyMessageKeyTokenizer(); return parseKey(keyTokenizer, key); } protected PropertyMessageKey parseKey(PropertyMessageKeyTokenizer kt, String key) throws PropertyMessageKey.MalformedKeyException { PropertyMessageKey mk = new PropertyMessageKey(); StringReader r = null; try { r = new StringReader(key); if (kt == null) kt = new PropertyMessageKeyTokenizer(); else kt.reset(); kt.setReader(r); int tokenCount = 0; int token = kt.nextToken(); while (token != StreamTokenizer.TT_EOF) { ++tokenCount; switch (token) { case PropertyMessageKeyTokenizer.TT_NUMBER: if (tokenCount == 2) { mk.setValidatorCode(kt.nval); if (kt.tokenLen != PropertyMessageKey.EXPECTED_VALIDATOR_CODE_LEN) throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", expected validator code length " + PropertyMessageKey.EXPECTED_VALIDATOR_CODE_LEN + ", actual " + kt.tokenLen); } else if (tokenCount == 4) { mk.setErrorCode(kt.nval); if (kt.tokenLen != PropertyMessageKey.EXPECTED_ERROR_CODE_LEN) throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", expected error code length " + PropertyMessageKey.EXPECTED_ERROR_CODE_LEN + ", actual " + kt.tokenLen); } else if (tokenCount == 6) mk.setErrorCodeFraction(kt.nval); else throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", unexpected number token: " + kt.nval); break; case PropertyMessageKeyTokenizer.TT_WORD: if (tokenCount == 1) mk.setValidatorId(kt.sval); else if (tokenCount == 3) mk.setSeverity(kt.sval); else throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", unexpected string token: " + kt.sval); break; case PropertyMessageKeyTokenizer.TT_FRACTION: if (tokenCount != 5) throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", unexpected character: " + (char) kt.ttype); break; default: throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", unexpected character: " + (char) kt.ttype); } token = kt.nextToken(); } } catch (IOException e) { throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", unexpected exception: " + e); } finally { if (r != null) r.close(); } try { mk.checkValidity(); } catch (PropertyMessageKey.MalformedKeyException e) { throw new PropertyMessageKey.MalformedKeyException(TKN01F001 + key + ", " + e); } return mk; } protected Error error(TestManager tm, TestType type, Category category, ContentType contentType, Reference reference, String subReference, String message, String messageKey) { Error.Severity severity = Error.Severity.UNSPECIFIED; try { PropertyMessageKey mk = getMsgKey(MSG_PREFIX + messageKey); category = verifyCategory(mk, category); severity = mk.getSeverity(); if (isResultFiltered(tm, messageKey, severity)) return null; } catch (PropertyMessageKey.MalformedKeyException e) { message = INTERNAL_ERROR_ID + e.toString(); } return errorInternal(tm, type, category, contentType, reference, subReference, message, messageKey, severity); } protected Error errorFormatterUnfiltered(TestManager tm, TestType type, Category category, ContentType contentType, Reference reference, String subReference, String messageKey, PropertyMessageKey mk, Object... messageArguments) { category = verifyCategory(mk, category); return errorInternal(tm, type, category, contentType, reference, subReference, msgFormatterNV(messageKey, messageArguments), messageKey, mk.getSeverity()); } private Category verifyCategory(PropertyMessageKey mk, Category category) { char sc = mk.getSeverityChar(); if (sc == 'Y' || sc == 'X') return Error.Category.INTERNAL; return category; } private Error errorInternal(TestManager tm, TestType type, Category category, ContentType contentType, Reference reference, String subReference, String message, String messageKey, Error.Severity severity) { return new Error(messageKey, type, category, severity, contentType, reference, message, subReference); } protected Progress progressFormatter(TestManager tm, String key, Object... formatArguments) { return progress(tm, key, true, formatArguments); } private Progress progress(TestManager tm, String key, Boolean useFormatter, Object... formatArguments) { if (properties != null) { try { PropertyMessageKey k = parseKey(key); if (tm != null) { if (tm.isFilteredProgressSeverity(k.getSeverity())) return null; if (tm.isFilteredProgressKey(key)) return null; } String msg = properties.getProperty(MSG_PREFIX + key); if (msg != null && msg.length() > 0) return new Progress(k.getSeverity(), useFormatter ? String.format(msg, formatArguments) : MessageFormat.format(msg, formatArguments)); } catch (PropertyMessageKey.MalformedKeyException e) { return new Progress(Error.Severity.ERROR, INTERNAL_ERROR_ID + e.toString()); } } return new Progress(Error.Severity.ERROR, INTERNAL_ERROR_ID + "missing message: " + key); } protected boolean isResultFiltered(TestManager tm, String messageKey, Error.Severity severity) { if (tm != null) { if (tm.isFilteredResultSeverity(severity)) return true; if (tm.isFilteredResultKey(messageKey)) return true; } return false; } }