View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text.translate;
18  
19  import java.io.IOException;
20  import java.io.Writer;
21  
22  /**
23   * Translates code points to their Unicode escaped value.
24   *
25   * @since 1.0
26   */
27  public class UnicodeEscaper extends CodePointTranslator {
28  
29      /**
30       * Constructs a {@code UnicodeEscaper} above the specified value (exclusive).
31       *
32       * @param codePoint above which to escape.
33       * @return The newly created {@code UnicodeEscaper} instance.
34       */
35      public static UnicodeEscaper above(final int codePoint) {
36          return outsideOf(0, codePoint);
37      }
38  
39      /**
40       * Constructs a {@code UnicodeEscaper} below the specified value (exclusive).
41       *
42       * @param codePoint below which to escape.
43       * @return The newly created {@code UnicodeEscaper} instance.
44       */
45      public static UnicodeEscaper below(final int codePoint) {
46          return outsideOf(codePoint, Integer.MAX_VALUE);
47      }
48  
49      /**
50       * Constructs a {@code UnicodeEscaper} between the specified values (inclusive).
51       *
52       * @param codePointLow above which to escape.
53       * @param codePointHigh below which to escape.
54       * @return The newly created {@code UnicodeEscaper} instance.
55       */
56      public static UnicodeEscaper between(final int codePointLow, final int codePointHigh) {
57          return new UnicodeEscaper(codePointLow, codePointHigh, true);
58      }
59  
60      /**
61       * Constructs a {@code UnicodeEscaper} outside of the specified values (exclusive).
62       *
63       * @param codePointLow below which to escape.
64       * @param codePointHigh above which to escape.
65       * @return The newly created {@code UnicodeEscaper} instance.
66       */
67      public static UnicodeEscaper outsideOf(final int codePointLow, final int codePointHigh) {
68          return new UnicodeEscaper(codePointLow, codePointHigh, false);
69      }
70  
71      /** The lowest code point boundary. */
72      private final int below;
73  
74      /** The highest code point boundary. */
75      private final int above;
76  
77      /** Whether to escape between the boundaries or outside them. */
78      private final boolean between;
79  
80      /**
81       * Constructs a {@code UnicodeEscaper} for all characters.
82       */
83      public UnicodeEscaper() {
84          this(0, Integer.MAX_VALUE, true);
85      }
86  
87      /**
88       * Constructs a {@code UnicodeEscaper} for the specified range. This is
89       * the underlying method for the other constructors/builders. The {@code below}
90       * and {@code above} boundaries are inclusive when {@code between} is
91       * {@code true} and exclusive when it is {@code false}.
92       *
93       * @param below int value representing the lowest code point boundary.
94       * @param above int value representing the highest code point boundary.
95       * @param between whether to escape between the boundaries or outside them.
96       */
97      protected UnicodeEscaper(final int below, final int above, final boolean between) {
98          this.below = below;
99          this.above = above;
100         this.between = between;
101     }
102 
103     /**
104      * Converts the given code point to a hexadecimal string of the form {@code "\\uXXXX"}.
105      *
106      * @param codePoint
107      *            a Unicode code point.
108      * @return The hexadecimal string for the given code point.
109      */
110     protected String toUtf16Escape(final int codePoint) {
111         return "\\u" + hex(codePoint);
112     }
113 
114     /**
115      * {@inheritDoc}
116      */
117     @Override
118     public boolean translate(final int codePoint, final Writer writer) throws IOException {
119         if (between) {
120             if (codePoint < below || codePoint > above) {
121                 return false;
122             }
123         } else if (codePoint >= below && codePoint <= above) {
124             return false;
125         }
126 
127         if (codePoint > 0xffff) {
128             writer.write(toUtf16Escape(codePoint));
129         } else {
130           writer.write("\\u");
131           writer.write(HEX_DIGITS[codePoint >> 12 & 15]);
132           writer.write(HEX_DIGITS[codePoint >> 8 & 15]);
133           writer.write(HEX_DIGITS[codePoint >> 4 & 15]);
134           writer.write(HEX_DIGITS[codePoint & 15]);
135         }
136         return true;
137     }
138 }