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

org.daisy.dotify.common.text.ConditionalMapper Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
package org.daisy.dotify.common.text;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Provides a character mapper which is activated when a specific character
 * is encountered. The mapping continues until the sequence is interrupted
 * by a character that is not in the map.
 *
 * @author Joel Håkansson
 */
public class ConditionalMapper {
    private final int trigger;
    private final Map map;

    /**
     * Creates a new mapper builder.
     */
    public static class Builder {
        private int trigger = -1;
        private final Map map;

        /**
         * Creates a builder with no specified trigger character.
         * Unless a trigger is specified using {@link #trigger(char)}
         * or {@link #trigger(int)}, the mapper will always be active.
         */
        public Builder() {
            this.map = new HashMap<>();
        }

        /**
         * Sets a trigger that activates the character mapper.
         *
         * @param trigger the trigger
         * @return returns this builder
         */
        public Builder trigger(char trigger) {
            return trigger((int) trigger);
        }

        /**
         * Sets a trigger that activates the character mapper.
         *
         * @param trigger the trigger
         * @return returns this builder
         */
        public Builder trigger(int trigger) {
            this.trigger = trigger;
            return this;
        }

        /**
         * 

Defines that characters found in the replace string are to be * replaced by the character at the corresponding position in the with * string.

* *

If there is a character in the replace string with no character * at a corresponding position in the with string (because * replace is longer than with), then that character * will be replaced by an empty string.

* *

If a character occurs more than once in the replace string, then the * last occurrence determines the replacement character.

* *

If the with string is longer than the second argument string, * then excess characters are ignored.

* *

Calling map("abc", "ABC") is equal to calling:

*
         * put('a', "A");
         * put('b', "B");
         * put('c', "C");
         * 
* *

Note however that {@link #put(int, String)} and {@link #put(char, String)} also * allow a replacement that contains more than one character, which {@link #map(String, String)} * does not.

* * @param replace a list of characters to replace * @param with a list of replacement characters * @return returns this builder */ public Builder map(String replace, String with) { Iterator chars = replace.codePoints().iterator(); int[] repl = with.codePoints().toArray(); int i = 0; while (chars.hasNext()) { int c = chars.next(); if (repl.length > i) { put(c, new String(Character.toChars(repl[i]))); } else { put(c, ""); } i++; } return this; } /** * Defines a replacement string for the specified character. * * @param key the character * @param value the replacement * @return returns this builder */ public Builder put(char key, String value) { return put((int) key, value); } /** * Defines a replacement string for the specified Unicode code point. * * @param key the code point * @param value the replacement * @return returns this builder */ public Builder put(int key, String value) { map.put(key, value); return this; } /** * Adds a transparent mapping for the specified character. In other words, * this character will not be replaced, but will not cause the mapper to * deactivate either. * * @param value the character * @return returns this builder */ public Builder putIgnorable(char value) { return putIgnorable((int) value); } /** * Adds a transparent mapping for the specified Unicode code point. * In other words, this code point will not be replaced, but will * not cause the mapper to deactivate either. * * @param value the character * @return returns this builder */ public Builder putIgnorable(int value) { map.put(value, new String(Character.toChars(value))); return this; } /** * Builds the mapper with the current configuration. * * @return returns a new mapper instance */ public ConditionalMapper build() { return new ConditionalMapper(this); } } /** * Creates a new builder with the specified trigger. * * @param trigger the trigger * @return returns a new builder instance */ public static Builder withTrigger(char trigger) { return new Builder().trigger(trigger); } /** * Creates a new builder with the specified trigger. * * @param trigger the trigger * @return returns a new builder instance */ public static Builder withTrigger(int trigger) { return new Builder().trigger(trigger); } /** *

Replaces in the input string occurrences of characters listed in * the replace string with the character at the corresponding * position in the with string.

* *

This method is intentionally similar to the translate XPath * function.

* *

For example, translate( "bar", "abc", "ABC") returns the string BAr.

* *

If there is a character in the replace string with no character * at a corresponding position in the with string (because * replace is longer than with), then occurrences of * that character in the input are removed.

* *

For example, translate( "--aaa--", "abc-", "ABC") returns "AAA".

* *

If a character occurs more than once in the replace string, then the * last occurrence determines the replacement character. This is unlike the * translate XPath function, which does the opposite.

* *

If the with string is longer than the second argument string, * then excess characters are ignored.

* * @param input the input * @param replace a list of characters to replace * @param with a list of new characters * @return returns the new string */ public static String translate(String input, String replace, String with) { return new Builder().map(replace, with).build().replace(input); } private ConditionalMapper(Builder builder) { this.trigger = builder.trigger; this.map = Collections.unmodifiableMap(new HashMap<>(builder.map)); } /** * Replaces characters in the input according to the rules of this mapper. * * @param input the input * @return returns the modified string */ public String replace(String input) { StringBuilder ret = new StringBuilder(); boolean active = false; Iterator chars = input.codePoints().iterator(); while (chars.hasNext()) { int current = chars.next(); if (active || trigger < 0) { String replacement = map.get(current); if (replacement == null) { active = false; ret.appendCodePoint(current); } else { ret.append(replacement); } } else { //leave as it is ret.appendCodePoint(current); } if (current == trigger) { active = true; } } return ret.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy