/*********************************************************************************
* The contents of this file are subject to the Common Public Attribution
* License Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.openemm.org/cpal1.html. The License is based on the Mozilla
* Public License Version 1.1 but Sections 14 and 15 have been added to cover
* use of software over a computer network and provide for limited attribution
* for the Original Developer. In addition, Exhibit A has been modified to be
* consistent with Exhibit B.
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is OpenEMM.
* The Original Developer is the Initial Developer.
* The Initial Developer of the Original Code is AGNITAS AG. All portions of
* the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights
* Reserved.
*
* Contributor(s): AGNITAS AG.
********************************************************************************/
package org.agnitas.util;
import org.agnitas.beans.DynamicTag;
import org.agnitas.beans.DynamicTagContent;
import org.agnitas.beans.Mailing;
import org.agnitas.beans.MailingComponent;
import org.agnitas.beans.MediatypeEmail;
import org.agnitas.exceptions.CharacterEncodingValidationException;
import org.agnitas.exceptions.CharacterEncodingValidationExceptionMod;
import org.agnitas.exceptions.EncodingError;
import org.agnitas.web.MailingContentForm;
import org.agnitas.web.forms.MailingBaseForm;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This class validates the content of a mailing against the character set
* defined in the mailing.
*
*
* @author Markus Dörschmidt
*
*/
public class CharacterEncodingValidatorImpl implements CharacterEncodingValidator {
/**
* Validates text and HTML template of the given form and the content blocks of the given Mailing object. This method is called directly <b>before modifying</b>
* the MailingBaseForm object.
*
* @param form form to validate text and HTML template
* @param mailing mailing to validate content blocks
*
*/
@Override
public void validate( MailingBaseForm form, Mailing mailing) throws CharacterEncodingValidationException {
Set<String> unencodeableMailingComponents = new HashSet<String>();
Set<String> unencodeableDynamicTags = new HashSet<String>();
boolean subjectValid = validate( form, mailing, unencodeableMailingComponents, unencodeableDynamicTags);
if( unencodeableMailingComponents.size() > 0 || unencodeableDynamicTags.size() > 0 || !subjectValid)
throw new CharacterEncodingValidationException( subjectValid, unencodeableMailingComponents, unencodeableDynamicTags);
}
private boolean validate( MailingBaseForm form, Mailing mailing, Set<String> unencodeableMailingComponents, Set<String> unencodeableDynamicTags) {
CharsetEncoder charsetEncoder = getCharsetEncoder( form);
validate( form, unencodeableMailingComponents, charsetEncoder);
validateDynamicTags( mailing, unencodeableDynamicTags, charsetEncoder);
return validate( form.getEmailSubject(), charsetEncoder);
}
@Override
public void validate( MailingContentForm form, Mailing mailing) throws CharacterEncodingValidationException {
Set<String> unencodeableDynamicTags = new HashSet<String>();
validate( form, mailing, unencodeableDynamicTags);
if( unencodeableDynamicTags.size() > 0)
throw new CharacterEncodingValidationException( new HashSet<String>(), unencodeableDynamicTags);
}
@Override
public void validate( MailingContentForm form, String charset) throws CharacterEncodingValidationException {
Set<String> unencodeableDynamicTags = new HashSet<String>();
validate( form, getCharsetEncoder(charset), unencodeableDynamicTags);
if( unencodeableDynamicTags.size() > 0)
throw new CharacterEncodingValidationException( new HashSet<String>(), unencodeableDynamicTags);
}
@Override
public void validateContentMod( MailingContentForm form, String charset) throws CharacterEncodingValidationExceptionMod {
Set<EncodingError> unencodeableDynamicTags = new HashSet<EncodingError>();
Map<String, DynamicTagContent> dynTags = form.getContent();
CharsetEncoder charsetEncoder = getCharsetEncoder(charset);
for (DynamicTagContent dynamicTagContent : dynTags.values()) {
Set<EncodingError> dynTagErrors = validateMod(dynamicTagContent.getDynContent(), charsetEncoder);
if (dynTagErrors.size() > 0) {
for (EncodingError error : dynTagErrors) {
unencodeableDynamicTags.add(new EncodingError(form.getDynName(), error.getLine(), error.getColumn(), error.getInvalidChar()));
}
}
}
if(unencodeableDynamicTags.size() > 0)
throw new CharacterEncodingValidationExceptionMod( new HashSet<EncodingError>(), new HashSet<EncodingError>(), unencodeableDynamicTags);
}
private void validate( MailingContentForm form, Mailing mailing, Set<String> unencodeableDynamicTags) {
CharsetEncoder charsetEncoder = getCharsetEncoder( mailing);
validate( form, charsetEncoder, unencodeableDynamicTags);
}
private void validate( MailingContentForm form, CharsetEncoder charsetEncoder, Set<String> unencodeableDynamicTags) {
Map<String, DynamicTagContent> dynTags = form.getContent();
for( DynamicTagContent dynamicTagContent : dynTags.values()) {
if( !validate(dynamicTagContent, charsetEncoder))
unencodeableDynamicTags.add( form.getDynName());
}
}
/**
* Validates a mailing component.
*
* @param component the mailing component to be validated
* @param charsetName the name of the character set to be used
* @return true if the mailing component passed validated otherwise false
*/
@Override
public boolean validate( MailingComponent component, String charsetName) {
CharsetEncoder charsetEncoder = getCharsetEncoder( charsetName);
return validate( component, charsetEncoder);
}
/**
* Validates a dynamic tag.
*
* @param dynTag DynamicTag to be validated
* @param charsetName character set to be used for validation
* @return true if the DynamicTag passed validated otherwise false
*/
@Override
public boolean validate( DynamicTag dynTag, String charsetName) {
CharsetEncoder charsetEncoder = getCharsetEncoder( charsetName);
return validate( dynTag, charsetEncoder);
}
private void validate( MailingBaseForm form, Set<String> unencodeableMailingComponents, CharsetEncoder encoder) {
if( !validate( form.getTextTemplate(), encoder))
unencodeableMailingComponents.add( "agnText");
if( !validate( form.getHtmlTemplate(), encoder))
unencodeableMailingComponents.add( "agnHtml");
}
private void validateDynamicTags( Mailing mailing, Set<String> unencodeableDynamicTags, CharsetEncoder charsetEncoder) {
// No mailing? Nothing to validate!
if( mailing == null)
return;
Collection<DynamicTag> dynTags = mailing.getDynTags().values();
for( DynamicTag dynTag : dynTags)
if( !validate( dynTag, charsetEncoder))
unencodeableDynamicTags.add( dynTag.getDynName());
}
/**
* Validates a mailing component using the given CharsetEncoder.
*
* @param component the mailing component to be validated
* @param charsetEncoder CharsetEncoder to be used for validation
* @return true if the mailing component passed validated otherwise false
*/
private boolean validate( MailingComponent component, CharsetEncoder charsetEncoder) {
return validate( component.getEmmBlock(), charsetEncoder);
}
/**
* Validates a dynamic tag using the given CharsetEncoder.
*
* @param dynTag DynamicTag to be validated
* @param charsetEncoder CharsetEncoder to be used for validation
* @return true if the DynamicTag passed validated otherwise false
*/
private boolean validate( DynamicTag dynTag, CharsetEncoder charsetEncoder) {
Collection<DynamicTagContent> contents = dynTag.getDynContent().values();
for( DynamicTagContent content : contents)
if( !validate( content, charsetEncoder))
return false;
return true;
}
/**
* Validates the content of a DynamicTagContent objects
* @param content object to be validates
* @param charsetEncoder CharacterEncoder to be used for validation
* @return
*/
private boolean validate( DynamicTagContent content, CharsetEncoder charsetEncoder) {
return validate( content.getDynContent(), charsetEncoder);
}
/**
* Validates a String using a CharsetEncoder.
*
* @param string String to be validated
* @param charsetEncoder CharsetEncoder to be used for validation
* @return true if the String passed the validation otherwise false
*/
private boolean validate( String string, CharsetEncoder charsetEncoder) {
return charsetEncoder.canEncode( string);
}
/**
* Returns a CharsetEncoder matching the character set defined in the given mailing.
* @param mailing Mailing to create CharsetEncoder for
* @return CharsetEncoder for mailing
*/
private CharsetEncoder getCharsetEncoder( Mailing mailing) {
String charsetName = ((MediatypeEmail) mailing.getMediatypes().get( 0)).getCharset();
return getCharsetEncoder( charsetName);
}
private CharsetEncoder getCharsetEncoder( MailingBaseForm form) {
return getCharsetEncoder( form.getEmailCharset());
}
/**
* Creates a CharsetEncoder for the given charset.
*
* @param charsetName name of character set
* @return CharsetEncoder for given charset
*/
private CharsetEncoder getCharsetEncoder( String charsetName) {
Charset charset = Charset.forName( charsetName);
return charset.newEncoder();
}
@Override
public void validateMod( MailingBaseForm form, Mailing mailing) throws CharacterEncodingValidationExceptionMod {
Set<EncodingError> subjectErrors = new HashSet<EncodingError>();
Set<EncodingError> unencodeableMailingComponents = new HashSet<EncodingError>();
Set<EncodingError> unencodeableDynamicTags = new HashSet<EncodingError>();
validateMod( form, mailing, subjectErrors, unencodeableMailingComponents, unencodeableDynamicTags);
if( unencodeableMailingComponents.size() > 0 || unencodeableDynamicTags.size() > 0 || subjectErrors.size() > 0)
throw new CharacterEncodingValidationExceptionMod( subjectErrors, unencodeableMailingComponents, unencodeableDynamicTags);
}
private void validateMod( MailingBaseForm form, Mailing mailing, Set<EncodingError> subjectErrors, Set<EncodingError> unencodeableMailingComponents, Set<EncodingError> unencodeableDynamicTags) {
CharsetEncoder charsetEncoder = getCharsetEncoder( form);
validateSubject(form,subjectErrors, charsetEncoder);
validateMod( form, unencodeableMailingComponents, charsetEncoder);
validateDynamicTagsMod(mailing, unencodeableDynamicTags, charsetEncoder);
}
private void validateSubject(MailingBaseForm form, Set<EncodingError> subjectErrors, CharsetEncoder encoder) {
subjectErrors.addAll(validateMod(form.getEmailSubject(), encoder));
}
private void validateMod( MailingBaseForm form, Set<EncodingError> unencodeableMailingComponents, CharsetEncoder encoder) {
Set<EncodingError> textTemplateErrors = validateMod( form.getTextTemplate(), encoder);
if(textTemplateErrors.size() > 0) {
for(EncodingError error : textTemplateErrors)
unencodeableMailingComponents.add(new EncodingError("agnText", error.getLine(), error.getColumn(), error.getInvalidChar()));
}
Set<EncodingError> htmlTemplateErrors = validateMod( form.getHtmlTemplate(), encoder);
if(htmlTemplateErrors.size() > 0) {
for(EncodingError error : htmlTemplateErrors)
unencodeableMailingComponents.add(new EncodingError("agnHtml", error.getLine(), error.getColumn(), error.getInvalidChar()));
}
}
private void validateDynamicTagsMod(Mailing mailing, Set<EncodingError> unencodeableDynamicTags, CharsetEncoder charsetEncoder) {
// No mailing? Nothing to validate!
if( mailing == null)
return;
Collection<DynamicTag> dynTags = mailing.getDynTags().values();
for( DynamicTag dynTag : dynTags) {
Collection<DynamicTagContent> contents = dynTag.getDynContent().values();
for( DynamicTagContent content : contents) {
Set<EncodingError> dynTagErrors = validateMod(content.getDynContent(), charsetEncoder);
if( dynTagErrors.size() > 0)
for(EncodingError error : dynTagErrors)
unencodeableDynamicTags.add(new EncodingError(dynTag.getDynName(), error.getLine(), error.getColumn(), error.getInvalidChar()));
}
}
}
private Set<EncodingError> validateMod(String string, CharsetEncoder charsetEncoder) {
String[] stringLines = string.split("\n");
Set<EncodingError> errors = new HashSet<EncodingError>();
for (int i = 0; i < stringLines.length; i++) {
for (int j = 0; j < stringLines[i].length(); j++) {
char charToValidate = stringLines[i].charAt(j);
if (!charsetEncoder.canEncode(charToValidate)) {
errors.add(new EncodingError(stringLines[i], i + 1, j + 1, charToValidate));
}
}
}
return errors;
}
}