001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.text.translate;
018
019import java.io.IOException;
020import java.io.Writer;
021
022import org.apache.commons.lang3.StringUtils;
023
024/**
025 * Translates code points to their Unicode escaped value.
026 *
027 * @since 3.0
028 * @deprecated As of <a href="https://commons.apache.org/proper/commons-lang/changes-report.html#a3.6">3.6</a>, use Apache Commons Text
029 * <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/translate/UnicodeEscaper.html">
030 * UnicodeEscaper</a>.
031 */
032@Deprecated
033public class UnicodeEscaper extends CodePointTranslator {
034
035    /**
036     * Constructs a {@link UnicodeEscaper} above the specified value (exclusive).
037     *
038     * @param codePoint above which to escape.
039     * @return The newly created {@link UnicodeEscaper} instance.
040     */
041    public static UnicodeEscaper above(final int codePoint) {
042        return outsideOf(0, codePoint);
043    }
044
045    /**
046     * Constructs a {@link UnicodeEscaper} below the specified value (exclusive).
047     *
048     * @param codePoint below which to escape.
049     * @return The newly created {@link UnicodeEscaper} instance.
050     */
051    public static UnicodeEscaper below(final int codePoint) {
052        return outsideOf(codePoint, Integer.MAX_VALUE);
053    }
054
055    /**
056     * Constructs a {@link UnicodeEscaper} between the specified values (inclusive).
057     *
058     * @param codePointLow above which to escape.
059     * @param codePointHigh below which to escape.
060     * @return The newly created {@link UnicodeEscaper} instance.
061     */
062    public static UnicodeEscaper between(final int codePointLow, final int codePointHigh) {
063        return new UnicodeEscaper(codePointLow, codePointHigh, true);
064    }
065
066    /**
067     * Constructs a {@link UnicodeEscaper} outside of the specified values (exclusive).
068     *
069     * @param codePointLow below which to escape.
070     * @param codePointHigh above which to escape.
071     * @return The newly created {@link UnicodeEscaper} instance.
072     */
073    public static UnicodeEscaper outsideOf(final int codePointLow, final int codePointHigh) {
074        return new UnicodeEscaper(codePointLow, codePointHigh, false);
075    }
076
077    private final int below;
078
079    private final int above;
080
081    private final boolean between;
082
083    /**
084     * Constructs a {@link UnicodeEscaper} for all characters.
085     */
086    public UnicodeEscaper() {
087        this(0, Integer.MAX_VALUE, true);
088    }
089
090    /**
091     * Constructs a {@link UnicodeEscaper} for the specified range. This is
092     * the underlying method for the other constructors/builders. The {@code below}
093     * and {@code above} boundaries are inclusive when {@code between} is
094     * {@code true} and exclusive when it is {@code false}.
095     *
096     * @param below int value representing the lowest code point boundary.
097     * @param above int value representing the highest code point boundary.
098     * @param between whether to escape between the boundaries or outside them.
099     */
100    protected UnicodeEscaper(final int below, final int above, final boolean between) {
101        this.below = below;
102        this.above = above;
103        this.between = between;
104    }
105
106    /**
107     * Converts the given code point to a hexadecimal string of the form {@code "\\uXXXX"}, or the
108     * surrogate pair form {@code "\\uXXXX\\uXXXX"} for a supplementary code point.
109     *
110     * @param codePoint
111     *            a Unicode code point.
112     * @return The hexadecimal string for the given code point.
113     * @since 3.2
114     */
115    protected String toUtf16Escape(final int codePoint) {
116        if (Character.isBmpCodePoint(codePoint)) {
117            return "\\u" + StringUtils.leftPad(hex(codePoint), 4, '0');
118        }
119        final char[] surrogatePair = Character.toChars(codePoint);
120        return "\\u" + hex(surrogatePair[0]) + "\\u" + hex(surrogatePair[1]);
121    }
122
123    /**
124     * {@inheritDoc}
125     */
126    @Override
127    public boolean translate(final int codePoint, final Writer out) throws IOException {
128        if (between) {
129            if (codePoint < below || codePoint > above) {
130                return false;
131            }
132        } else if (codePoint >= below && codePoint <= above) {
133            return false;
134        }
135
136        // TODO: Handle potential + sign per various Unicode escape implementations
137        if (codePoint > 0xffff) {
138            out.write(toUtf16Escape(codePoint));
139        } else {
140          out.write("\\u");
141          out.write(HEX_DIGITS[codePoint >> 12 & 15]);
142          out.write(HEX_DIGITS[codePoint >> 8 & 15]);
143          out.write(HEX_DIGITS[codePoint >> 4 & 15]);
144          out.write(HEX_DIGITS[codePoint & 15]);
145        }
146        return true;
147    }
148}