All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.sii.ogham.sms.builder.cloudhopper.EncoderBuilder Maven / Gradle / Ivy

The newest version!
package fr.sii.ogham.sms.builder.cloudhopper;

import static com.cloudhopper.commons.charset.CharsetUtil.NAME_GSM;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_GSM7;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_ISO_8859_1;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_UCS_2;

import com.cloudhopper.commons.charset.Charset;
import com.cloudhopper.commons.charset.CharsetUtil;

import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
import fr.sii.ogham.core.builder.configurer.Configurer;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.builder.env.EnvironmentBuilder;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.core.util.PriorizedList;
import fr.sii.ogham.sms.encoder.Encoder;
import fr.sii.ogham.sms.encoder.SupportingEncoder;
import fr.sii.ogham.sms.exception.message.EncodingException;
import fr.sii.ogham.sms.sender.impl.cloudhopper.encoder.CloudhopperCharsetSupportingEncoder;
import fr.sii.ogham.sms.sender.impl.cloudhopper.encoder.GuessEncodingEncoder;
import fr.sii.ogham.sms.sender.impl.cloudhopper.encoder.NamedCharset;

/**
 * Configures text message encoding:
 * 
 * It supports GSM 03.38
 * standard encodings. It automatically guess the best supported encoding in
 * order to use the minimum octets:
 * 
    *
  • It encodes using GSM 7-bit default alphabet if the message contains only * characters defined in the table. Message is packed so the message can have a * maximum length of 160 characters. This is enable only if automatic guessing * is enabled (using {@link #autoGuess(Boolean)}) and GSM 7-bit is enabled * (using {@link #gsm7bitPacked(Integer)}).
  • *
  • It encodes using GSM 8-bit data encoding if the message contains only * characters that can be encoded on one octet. This is enable only if automatic * guessing is enabled (using {@link #autoGuess(Boolean)}) and GSM 8-bit is * enabled (using {@link #gsm8bit(Integer)}).
  • *
  • It encodes using Latin 1 (ISO-8859-1) data encoding if the message * contains only characters that can be encoded on one octet. This is enable * only if automatic guessing is enabled (using {@link #autoGuess(Boolean)}) and * Latin-1 is enabled (using {@link #latin1(Integer)}).
  • *
  • It encodes using UCS-2 encoding if the message contains special * characters that can't be encoded on one octet. Each character is encoded on * two octets. This is enable only if automatic guessing is enabled (using * {@link #autoGuess(Boolean)}) and UCS-2 is enabled (using * {@link #ucs2(Integer)}).
  • *
* * Automatic guessing enabled *

* Standard encodings are registered with a priority. The priority is used when * auto-guessing is enabled. Each registered encoding is tested against the text * message starting with the encoding with the highest priority. *

* *

* If a priority is set to 0 (or negative number), the encoding is disabled. *

* *

* Any registered custom encoder is added into the guessing list according to * its priority. Use a the highest value to use custom encoder first. To know * default priority values for encodings, see * {@link DefaultCloudhopperConfigurer}. *

* * Automatic guessing disabled *

* Standard encodings are not registered at all. *

* *

* If custom encoders are registered then only those encoders are used. *

* *

* If no custom encoders are registered, then default charset encoding is used * (see {@link #fallback(String)}). *

* * * @author Aurélien Baudet * */ public class EncoderBuilder extends AbstractParent implements Builder { protected final BuildContext buildContext; protected final StandardEncodingHelper gsm7PackedValueBuilder; protected final StandardEncodingHelper gsm8ValueBuilder; protected final StandardEncodingHelper ucs2ValueBuilder; protected final StandardEncodingHelper latin1ValueBuilder; protected final PriorizedList customEncoders; protected final ConfigurationValueBuilderHelper autoGuessValueBuilder; protected final ConfigurationValueBuilderHelper fallbackCharsetNameValueBuilder; /** * Initializes the builder with a parent builder. The parent builder is used * when calling {@link #and()} method. The {@link EnvironmentBuilder} is * used to evaluate properties when {@link #build()} method is called. * * @param parent * the parent builder * @param buildContext * for registering instances and property evaluation */ public EncoderBuilder(CloudhopperBuilder parent, BuildContext buildContext) { super(parent); this.buildContext = buildContext; gsm7PackedValueBuilder = buildContext.newConfigurationValueBuilder(ctx -> new StandardEncodingHelper(this, NAME_GSM7, ctx)); gsm8ValueBuilder = buildContext.newConfigurationValueBuilder(ctx -> new StandardEncodingHelper(this, NAME_GSM, ctx)); ucs2ValueBuilder = buildContext.newConfigurationValueBuilder(ctx -> new StandardEncodingHelper(this, NAME_UCS_2, ctx)); latin1ValueBuilder = buildContext.newConfigurationValueBuilder(ctx -> new StandardEncodingHelper(this, NAME_ISO_8859_1, ctx)); customEncoders = new PriorizedList<>(); autoGuessValueBuilder = buildContext.newConfigurationValueBuilder(this, Boolean.class); fallbackCharsetNameValueBuilder = buildContext.newConfigurationValueBuilder(this, String.class); } /** * Set priority for encoding text messages using GSM 7-bit encoding. GSM * 7-bit encoding and GSM 8-bit encoding use the same character tables. Only * 7 bits are necessary to represents characters. In GSM 8-bit encoding a * leading 0 is added. However, GSM 7-bit encoding is packed. Every * character is "merged" with the next one in order to use more characters * for the same number of octets. * *

* If priority value is 0 or negative, it disables GSM 7-bit encoding. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #gsm7bitPacked()}. * *

	 * .gsm7bitPacked(10)
	 * .gsm7bitPacked()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(0)
	 * 
* *
	 * .gsm7bitPacked(10)
	 * .gsm7bitPacked()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(0)
	 * 
* * In both cases, {@code gsm7bitPacked(10)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param priority * the priority (highest value means that GSM 7-bit encoding is * tried first) * @return this instance for fluent chaining */ public EncoderBuilder gsm7bitPacked(Integer priority) { gsm7PackedValueBuilder.setValue(priority); return this; } /** * Set priority for encoding text messages using GSM 7-bit encoding. GSM * 7-bit encoding and GSM 8-bit encoding use the same character tables. Only * 7 bits are necessary to represents characters. In GSM 8-bit encoding a * leading 0 is added. However, GSM 7-bit encoding is packed. Every * character is "merged" with the next one in order to use more characters * for the same number of octets. * *

* If priority value is 0 or negative, it disables GSM 7-bit encoding. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .gsm7bitPacked()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(0)
	 * 
* *

* Non-null value set using {@link #gsm7bitPacked(Integer)} takes precedence * over property values and default value. * *

	 * .gsm7bitPacked(10)
	 * .gsm7bitPacked()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(0)
	 * 
* * The value {@code 10} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder gsm7bitPacked() { return gsm7PackedValueBuilder; } /** * Set priority for encoding text messages using GSM 8-bit encoding. GSM * 7-bit encoding and GSM 8-bit encoding use the same character tables. Only * 7 bits are necessary to represents characters. In GSM 8-bit encoding a * leading 0 is added. * *

* If priority value is 0 or negative, it disables GSM 8-bit encoding. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #gsm8bit()}. * *

	 * .gsm8bit(10)
	 * .gsm8bit()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(5)
	 * 
* *
	 * .gsm8bit(10)
	 * .gsm8bit()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(5)
	 * 
* * In both cases, {@code gsm8bit(10)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param priority * the priority (highest value means that GSM 8-bit encoding is * tried first) * @return this instance for fluent chaining */ public EncoderBuilder gsm8bit(Integer priority) { gsm8ValueBuilder.setValue(priority); return this; } /** * Set priority for encoding text messages using GSM 8-bit encoding. GSM * 7-bit encoding and GSM 8-bit encoding use the same character tables. Only * 7 bits are necessary to represents characters. In GSM 8-bit encoding a * leading 0 is added. * *

* If priority value is 0 or negative, it disables GSM 8-bit encoding. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .gsm8bit()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(5)
	 * 
* *

* Non-null value set using {@link #gsm8bit(Integer)} takes precedence over * property values and default value. * *

	 * .gsm8bit(10)
	 * .gsm8bit()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(5)
	 * 
* * The value {@code 10} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder gsm8bit() { return gsm8ValueBuilder; } /** * Set priority for encoding text messages using UCS-2. UCS-2 uses two * octets per character. * *

* If priority value is 0 or negative, it disables UCS-2 encoding. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #ucs2()}. * *

	 * .ucs2(10)
	 * .ucs2()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(2)
	 * 
* *
	 * .ucs2(10)
	 * .ucs2()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(2)
	 * 
* * In both cases, {@code ucs2(10)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param priority * the priority (highest value means that UCS-2 encoding is tried * first) * @return this instance for fluent chaining */ public EncoderBuilder ucs2(Integer priority) { ucs2ValueBuilder.setValue(priority); return this; } /** * Set priority for encoding text messages using UCS-2. UCS-2 uses two * octets per character. * *

* If priority value is 0 or negative, it disables UCS-2 encoding. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .ucs2()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(2)
	 * 
* *

* Non-null value set using {@link #ucs2(Integer)} takes precedence over * property values and default value. * *

	 * .ucs2(10)
	 * .ucs2()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(2)
	 * 
* * The value {@code 10} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder ucs2() { return ucs2ValueBuilder; } /** * Set priority for encoding text messages using Latin-1 (ISO-8859-1). * *

* If priority value is 0 or negative, it disables Latin-1 encoding. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #latin1()}. * *

	 * .latin1(10)
	 * .latin1()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(4)
	 * 
* *
	 * .latin1(10)
	 * .latin1()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(4)
	 * 
* * In both cases, {@code latin1(10)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param priority * the priority (highest value means that Latin-1 encoding is * tried first) * @return this instance for fluent chaining */ public EncoderBuilder latin1(Integer priority) { latin1ValueBuilder.setValue(priority); return this; } /** * Set priority for encoding text messages using Latin-1 (ISO-8859-1). * *

* If priority value is 0 or negative, it disables Latin-1 encoding. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .latin1()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(4)
	 * 
* *

* Non-null value set using {@link #latin1(Integer)} takes precedence over * property values and default value. * *

	 * .latin1(10)
	 * .latin1()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(4)
	 * 
* * The value {@code 10} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder latin1() { return latin1ValueBuilder; } /** * Register a custom {@link Encoder} with associated priority. * *

* The encoder is registered like standard encoders (see * {@link #gsm7bitPacked(Integer)}, {@link #gsm8bit(Integer)}, * {@link #latin1(Integer)}, {@link #ucs2(Integer)}). * *

* If automatic guessing is enabled (see {@link #autoGuess(Boolean)}), the * registered encoder is also used in automatic guessing (according to * priorities). * *

* If automatic guessing is disabled, only custom {@link Encoder}(s) that * are registered using this method are used. They are executed according to * priority order (highest priority is executed first). If encoder fails to * encode (throws {@link EncodingException}) then the next one is tried. The * registered encoder can also implement {@link SupportingEncoder} interface * to indicate if the encoder is able to encode or not the text. * *

* If priority is set to 0 (or negative number), the associated encoder is * disabled. * * @param encoder * the encoder to register * @param priority * the associated priority (the highest priority is executed * first) * @return this instance for fluent chaining */ public EncoderBuilder register(Encoder encoder, int priority) { customEncoders.register(encoder, priority); return this; } /** * Enable/disable automatic guessing of message encoding. * *

* If enables, it automatically guess the best supported encoding in order * to use the minimum octets: *

    *
  • It encodes using GSM 7-bit default alphabet if the message contains * only characters defined in the table. Message is packed so the message * can have a maximum length of 160 characters. This is enable only if * automatic guessing is enabled (using {@link #autoGuess(Boolean)}) and GSM * 7-bit is enabled (using {@link #gsm7bitPacked(Integer)}).
  • *
  • It encodes using GSM 8-bit data encoding if the message contains only * characters that can be encoded on one octet. This is enable only if * automatic guessing is enabled (using {@link #autoGuess(Boolean)} and GSM * 8-bit is enabled (using {@link #gsm8bit(Integer)}).
  • *
  • It encodes using Latin 1 (ISO-8859-1) data encoding if the message * contains only characters that can be encoded on one octet. This is enable * only if automatic guessing is enabled (using {@link #autoGuess(Boolean)} * and GSM 8-bit is enabled (using {@link #latin1(Integer)}).
  • *
  • It encodes using UCS-2 encoding if the message contains special * characters that can't be encoded on one octet. Each character is encoded * on two octets. This is enable only if automatic guessing is enabled * (using {@link #autoGuess(Boolean)}) and UCS-2 is enabled (using * {@link #ucs2(Integer)}).
  • *
* *

* The value set using this method takes precedence over any property and * default value configured using {@link #autoGuess()}. * *

	 * .autoGuess(false)
	 * .autoGuess()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* *
	 * .autoGuess(false)
	 * .autoGuess()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* * In both cases, {@code autoGuess(false)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param enable * enable or disable automatic guessing of encoding * @return this instance for fluent chaining */ public EncoderBuilder autoGuess(Boolean enable) { autoGuessValueBuilder.setValue(enable); return this; } /** * Enable/disable automatic guessing of message encoding. * *

* If enabled, it automatically guess the best supported encoding in order * to use the minimum octets: *

    *
  • It encodes using GSM 7-bit default alphabet if the message contains * only characters defined in the table. Message is packed so the message * can have a maximum length of 160 characters. This is enabled only if * automatic guessing is enabled (using {@link #autoGuess(Boolean)}) and GSM * 7-bit is enabled (using {@link #gsm7bitPacked(Integer)}).
  • *
  • It encodes using GSM 8-bit data encoding if the message contains only * characters that can be encoded on one octet. This is enabled only if * automatic guessing is enabled (using {@link #autoGuess(Boolean)} and GSM * 8-bit is enabled (using {@link #gsm8bit(Integer)}).
  • *
  • It encodes using Latin 1 (ISO-8859-1) data encoding if the message * contains only characters that can be encoded on one octet. This is * enabled only if automatic guessing is enabled (using * {@link #autoGuess(Boolean)} and Latin-1 is enabled (using * {@link #latin1(Integer)}).
  • *
  • It encodes using UCS-2 encoding if the message contains special * characters that can't be encoded on one octet. Each character is encoded * on two octets. This is enabled only if automatic guessing is enabled * (using {@link #autoGuess(Boolean)}) and UCS-2 is enabled (using * {@link #ucs2(Integer)}).
  • *
* *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .autoGuess()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* *

* Non-null value set using {@link #autoGuess(Boolean)} takes precedence * over property values and default value. * *

	 * .autoGuess(false)
	 * .autoGuess()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* * The value {@code false} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder autoGuess() { return autoGuessValueBuilder; } /** * Set which Cloudhopper {@link Charset} should be used if nothing else is * configured. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #fallback()}. * *

	 * .fallback(CharsetUtil.NAME_GSM8)
	 * .fallback()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(CharsetUtil.NAME_GSM)
	 * 
* *
	 * .fallback(CharsetUtil.NAME_GSM8)
	 * .fallback()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(CharsetUtil.NAME_GSM)
	 * 
* * In both cases, {@code fallback(CharsetUtil.NAME_GSM8)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param charsetName * the name of the charset to use (see {@link CharsetUtil}) * @return this instance for fluent chaining */ public EncoderBuilder fallback(String charsetName) { fallbackCharsetNameValueBuilder.setValue(charsetName); return this; } /** * Set which Cloudhopper {@link Charset} should be used if nothing else is * configured. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .fallback()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(CharsetUtil.NAME_GSM)
	 * 
* *

* Non-null value set using {@link #fallback(String)} takes precedence over * property values and default value. * *

	 * .fallback(CharsetUtil.NAME_GSM8)
	 * .fallback()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(CharsetUtil.NAME_GSM)
	 * 
* * The value {@code CharsetUtil.NAME_GSM8} is used regardless of the value * of the properties and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder fallback() { return fallbackCharsetNameValueBuilder; } @Override public Encoder build() { if (autoGuessEnabled()) { return buildAutoGuessEncoder(); } if (customEncodersRegistered()) { return buildContext.register(new GuessEncodingEncoder(customEncoders.getOrdered())); } String fallbackCharsetName = fallbackCharsetNameValueBuilder.getValue(); return buildFixedEncoder(fallbackCharsetName == null ? NAME_GSM : fallbackCharsetName); } protected boolean autoGuessEnabled() { return autoGuessValueBuilder.getValue(false); } private boolean customEncodersRegistered() { return !customEncoders.isEmpty(); } private Encoder buildAutoGuessEncoder() { PriorizedList registry = new PriorizedList<>(); registerStandardEncoder(gsm7PackedValueBuilder, registry); registerStandardEncoder(gsm8ValueBuilder, registry); registerStandardEncoder(latin1ValueBuilder, registry); registerStandardEncoder(ucs2ValueBuilder, registry); registry.register(customEncoders); return buildContext.register(new GuessEncodingEncoder(registry.getOrdered())); } private Encoder buildFixedEncoder(String charsetName) { return buildContext.register(new CloudhopperCharsetSupportingEncoder(NamedCharset.from(charsetName))); } private void registerStandardEncoder(StandardEncodingHelper helper, PriorizedList registry) { Integer priority = helper.getValue(); if (priority == null || priority <= 0) { return; } registry.register(buildContext.register(new CloudhopperCharsetSupportingEncoder(helper.getCharset())), priority); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy