Package com.kre8orz.i18n.processor

Source Code of com.kre8orz.i18n.processor.I18NVisitor$MsgInfo

/* Copyright Tom Valine 2002,2014 All Rights Reserved. ****************************************************************/
package com.kre8orz.i18n.processor;

import com.kre8orz.i18n.annotation.I18NMessage;
import com.kre8orz.i18n.annotation.I18NMessages;
import com.kre8orz.i18n.annotation.I18NResourceBundle;
import java.util.*;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor7;
import javax.tools.Diagnostic.Kind;

import static com.kre8orz.i18n.processor.I18NProcessorConstants.*;
import static com.kre8orz.i18n.processor.I18NProcessorMessages.*;

/**
* The visitor used during annotation processing to collect the detailed information regarding messages and resource
* bundles. The visitor pattern is the method adopted by Java for the processing of elements during annotation
* processing.
*
* @author  Tom Valine (thomas.valine@gmail.com)
* @see     SimpleElementVisitor7
*/
final class I18NVisitor extends SimpleElementVisitor7<Void, Void> {

    //~ Static fields/initializers *************************************************************************************

    /** A table of hex digits. */
    private static final char[] hexDigit = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };

    //~ Methods ********************************************************************************************************

    private static String convertComment(String comments) {
        StringBuilder sb = new StringBuilder("#");
        int len = comments.length();
        int current = 0;
        int last = 0;
        char[] uu = new char[6];

        uu[0] = '\\';
        uu[1] = 'u';
        while (current < len) {
            char c = comments.charAt(current);

            if ((c > '\u00ff') || (c == '\n') || (c == '\r')) {
                if (last != current) {
                    sb.append(comments.substring(last, current));
                }
                if (c > '\u00ff') {
                    uu[2] = toHex((c >> 12) & 0xf);
                    uu[3] = toHex((c >> 8) & 0xf);
                    uu[4] = toHex((c >> 4) & 0xf);
                    uu[5] = toHex(c & 0xf);
                    sb.append(new String(uu));
                } else {
                    sb.append("\n");
                    if ((c == '\r') && (current != (len - 1)) && (comments.charAt(current + 1) == '\n')) {
                        current++;
                    }
                    if ((current == (len - 1)) ||
                            ((comments.charAt(current + 1) != '#') && (comments.charAt(current + 1) != '!'))) {
                        sb.append("#");
                    }
                }
                last = current + 1;
            }
            current++;
        } // end while
        if (last != current) {
            sb.append(comments.substring(last, current));
        }
        return sb.toString();
    }

    /**
     * Convert a nibble to a hex character.
     *
     * @param   nibble  the nibble to convert.
     *
     * @return  convert a nibble to a hex character.
     */
    private static char toHex(int nibble) {
        return hexDigit[(nibble & 0xF)];
    }

    //~ Instance fields ************************************************************************************************

    private final I18NProcessorMessages _i18n;
    private final List<MsgInfo> _msgInfo;
    private final ProcessingEnvironment _pe;

    //~ Constructors ***************************************************************************************************

    /**
     * Creates a new I18NVisitor object.
     *
     * @param  pe    The current processing environment supplied by the compiler or other processor host if not running
     *               via a compiler.
     * @param  i18n  An instance of the processor messages class. This class is used to retrieve and format log messages
     *               if needed.
     */
    I18NVisitor(ProcessingEnvironment pe, I18NProcessorMessages i18n) {
        _pe = pe;
        _msgInfo = new ArrayList<MsgInfo>();
        _i18n = i18n;
    }

    //~ Methods ********************************************************************************************************

    /**
     * Used to obtain the complete list of processed annotation information. This information is used by the annotation
     * processor during bundle and catalog generation.
     *
     * @return  The list of processed annotation information.
     */
    public List<MsgInfo> getMsgInfo() {
        return Collections.unmodifiableList(_msgInfo);
    }

    /**
     * Dispatch method for variable elements. This method specifically processes <tt>I18NMessages</tt> and <tt>
     * I18NMessage</tt> annotations. As part of this processing, the annotated field is checked to ensure that it is a
     * <tt>String</tt> constant (e.g. static final String).
     *
     * @param   ele {@inheritDoc}
     * @param   par {@inheritDoc}
     *
     * @return  Nothing as the visitor simply processes the annotations during round processing.
     *
     * @see     javax.lang.model.util.SimpleElementVisitor6
     */
    @Override
    public Void visitVariable(VariableElement ele, Void par) {
        I18NMessage msg = ele.getAnnotation(I18NMessage.class);
        I18NMessages msgs = ele.getAnnotation(I18NMessages.class);

        if (_checkModifiers(ele)) {
            if (msgs != null) {
                for (I18NMessage ann : msgs.value()) {
                    _msgInfo.add(new MsgInfo(ele, ann));
                }
            }
            if (msg != null) {
                _msgInfo.add(new MsgInfo(ele, msg));
            }
        } else {
            _i18n.record(Kind.WARNING, STRING_CONSTANT_REQUIRED, ele);
        }
        return null;
    }

    /* Helper method used to ensure that the i18n annotations are attached only
     * to string constants. */
    private boolean _checkModifiers(VariableElement ele) {
        Set<Modifier> modifiers = ele.getModifiers();

        return modifiers.contains(Modifier.FINAL) && modifiers.contains(Modifier.STATIC) &&
            (ele.getConstantValue() != null) && !ele.asType().getKind().isPrimitive();
    }

    private String convertValue(String theString, boolean isKey) {
        int len = theString.length();
        int bufLen = len * 2;

        if (bufLen < 0) {
            bufLen = Integer.MAX_VALUE;
        }

        StringBuilder outBuffer = new StringBuilder(bufLen);

        for (int x = 0; x < len; x++) {
            char aChar = theString.charAt(x);

            // Handle common case first, selecting largest block that
            // avoids the specials below
            if ((aChar > 61) && (aChar < 127)) {
                if (aChar == '\\') {
                    outBuffer.append('\\');
                    outBuffer.append('\\');
                    continue;
                }
                outBuffer.append(aChar);
                continue;
            }
            switch (aChar) {
                case ' ': {
                    if ((x == 0) || isKey) {
                        outBuffer.append('\\');
                    }
                    outBuffer.append(' ');
                    break;
                }
                case '\t':
                case '\n':
                case '\r':
                case '\f':
                case '=':
                case ':':
                case '#':
                case '!': {
                    outBuffer.append(aChar);
                    break;
                }
                default: {
                    if ((aChar < 0x0020) || (aChar > 0x007e)) {
                        outBuffer.append('\\');
                        outBuffer.append('u');
                        outBuffer.append(toHex((aChar >> 12) & 0xF));
                        outBuffer.append(toHex((aChar >> 8) & 0xF));
                        outBuffer.append(toHex((aChar >> 4) & 0xF));
                        outBuffer.append(toHex(aChar & 0xF));
                    } else {
                        outBuffer.append(aChar);
                    }
                }
            } // end switch
        } // end for
        return outBuffer.toString();
    }

    //~ Inner Classes **************************************************************************************************

    /**
     * Helper class used to elaborate an individual <tt>I18NMessage</tt> annotation and encapsulate the distilled
     * message information.
     *
     * @author  Tom Valine (thomas.valine@gmail.com)
     */
    class MsgInfo {

        private String _baseName;
        private String _catalogKey;
        private final String _help;
        private final String _key;
        private final String _locale;
        private String _pkg;
        private final String _value;

        private MsgInfo(VariableElement ele, I18NMessage msg) {
            _calculateNames(ele, msg);
            _calculateCatalogKey(ele);
            this._key = ele.getConstantValue().toString();
            this._value = msg.value();
            this._help = msg.note();
            this._locale = msg.locale();
        }

        /**
         * Returns the base name of the bundle to which this message should belong.
         *
         * @return  The base name of the bundle to which this message should belong.
         */
        String getBaseName() {
            return _baseName;
        }

        /**
         * Returns the catalog key that can be used to obtain the path to the resource bundle to which this message
         * belongs. This resolves to the simple name of the interface class in which the original annotated field was
         * declared.
         *
         * @return  The bundle key used to obtain the path to the generated resource bundle to which this message
         *          belongs.
         */
        String getCatalogKey() {
            return _catalogKey;
        }

        /**
         * Returns the help note specified in the message annotation. This value is used as the comment for the message
         * in the corresponding generated resource bundle.
         *
         * @return  The help note specified for the message.
         */
        String getHelp() {
            return ((_help == null) || _help.isEmpty()) ? "" : convertComment(_help);
        }

        /**
         * Returns the bundle key name for this message. This value is used directly as the key name for the message in
         * the generated bundle class.
         *
         * @return  The bundle key name for this message.
         */
        String getKey() {
            return convertValue(_key, true);
        }

        /**
         * Returns the locale for which this message translation is to be used. This value is used to determine which
         * locale specific generated bundle should contain this message translation.
         *
         * @return  The locale for which this message translation is to be used.
         */
        String getLocale() {
            return _locale;
        }

        /**
         * Returns the package name for the generated bundle in which this message is contained.
         *
         * @return  The package name for the generated bundle.
         */
        String getPkg() {
            return _pkg;
        }

        /**
         * Returns the unformatted message string.
         *
         * @param   rawValue  Return a raw value rather than escaped Unicode.
         *
         * @return  The unformatted message string.
         */
        String getValue(boolean rawValue) {
            return rawValue ? _value : convertValue(_value, false);
        }

        /**
         * Indicates whether or not a message is in fact a locale specific translation of the base message.
         *
         * @return  True if the message is a locale specific translation.
         */
        boolean isLocalized() {
            return (!_locale.trim().isEmpty());
        }

        /* Helper method used to calculate the value of the key used in the
         * catalog class. */
        private void _calculateCatalogKey(VariableElement ele) {
            String catKey = ele.getEnclosingElement().getSimpleName().toString();
            PackageElement pack = _pe.getElementUtils().getPackageOf(ele);

            if (!pack.isUnnamed()) {
                String sep = DOT_SEPARATOR;

                catKey = pack.getQualifiedName() + sep + catKey;
            }
            this._catalogKey = catKey;
        }

        /* Helper method used to calculate the base name and package name for
         * the bundle into which this annotation will have it's information
         * written to. */
        private void _calculateNames(VariableElement ele, I18NMessage msg) {
            Class<I18NResourceBundle> bClz = I18NResourceBundle.class;
            I18NResourceBundle rb = ele.getEnclosingElement().getAnnotation(bClz);
            String name;
            String pack;

            if (rb != null) {
                name = rb.bundleName();
                pack = rb.packageName();
            } else {
                Elements eUtils = _pe.getElementUtils();

                pack = eUtils.getPackageOf(ele).getQualifiedName().toString();
                name = ele.getEnclosingElement().getSimpleName().toString();
            }
            if (!msg.locale().isEmpty()) {
                name += UNDERSCORE + msg.locale();
            }
            name += PROPERTY_FILE_SUFFIX;
            _baseName = name;
            _pkg = pack;
        }
    }
}
/* Copyright Tom Valine 2002,2014 All Rights Reserved. ****************************************************************/ 
TOP

Related Classes of com.kre8orz.i18n.processor.I18NVisitor$MsgInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.