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

org.zanata.adapter.properties.PropReader Maven / Gradle / Ivy

The newest version!
package org.zanata.adapter.properties;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collections;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Map;

import org.fedorahosted.openprops.Properties;
import org.zanata.common.ContentState;
import org.zanata.common.LocaleId;
import org.zanata.rest.dto.extensions.comment.SimpleComment;
import org.zanata.rest.dto.resource.Resource;
import org.zanata.rest.dto.resource.TextFlow;
import org.zanata.rest.dto.resource.TextFlowTarget;
import org.zanata.rest.dto.resource.TranslationsResource;
import org.zanata.util.HashUtil;

import com.google.common.collect.Maps;

/**
 * A Properties reader with support for skipping NON-TRANSLATABLE keys. NOT
 * THREADSAFE.
 *
 * @author Sean Flanigan
 * @version $Revision: 1.1 $
 */
public class PropReader {
    public static final String PROP_CONTENT_TYPE = "text/plain";
    private static final String NEWLINE_REGEX = "(\r\n|\r|\n)";

    private final PropWriter.CHARSET charset;
    private final LocaleId sourceLocale;
    private final ContentState contentState;

    /**
     * @param charset
     *            charset to use when reading .properties files (usually
     *            "ISO-8859-1")
     * @param sourceLocale
     *            "lang" attribute for source TextFlows
     * @param contentState
     *            ContentState for new TextFlowTargets (typically Approved)
     */
    public PropReader(PropWriter.CHARSET charset, LocaleId sourceLocale,
            ContentState contentState) {
        this.charset = charset;
        this.sourceLocale = sourceLocale;
        this.contentState = contentState;
    }

    public PropReader() {
        this(PropWriter.CHARSET.Latin1, LocaleId.EN_US, ContentState.Translated);
    }

    // pre: template already extracted
    @Deprecated
    public void extractTarget(TranslationsResource doc, InputStream in)
            throws IOException, RuntimeException {
        extractTarget(doc, in, Collections.>emptyMap());
    }

    public void extractTarget(TranslationsResource doc, InputStream in,
            Resource srcDoc) throws IOException, RuntimeException {
        Map> keyToSourceContentsMap = Maps.newHashMap();
        for (TextFlow tf: srcDoc.getTextFlows()) {
            keyToSourceContentsMap.put(tf.getId(), tf.getContents());
        }
        extractTarget(doc, in, keyToSourceContentsMap);
    }

    private void extractTarget(TranslationsResource doc, InputStream in,
            Map> keyToSourceContentsMap) throws IOException, RuntimeException {
        Properties props = loadProps(in);
        for (String key : props.keySet()) {
            List sourceContents = keyToSourceContentsMap.get(key);
            String sourceHash = sourceContents == null ? null : HashUtil.sourceHash(sourceContents);
            addPropEntryToDoc(doc, props, key, contentState, sourceHash);
        }
    }

    private void addPropEntryToDoc(TranslationsResource doc, Properties props,
            String key, ContentState contentState, String sourceHash) {
        String content = props.getProperty(key);
        if (content == null)
            return;
        TextFlowTarget textFlowTarget = new TextFlowTarget(key);
        textFlowTarget.setSourceHash(sourceHash);
        textFlowTarget.setContents(content);
        if (!content.isEmpty()) {
            textFlowTarget.setState(contentState);
        } else {
            textFlowTarget.setState(ContentState.New);
        }
        String comment = props.getComment(key);
        if (comment != null && comment.length() != 0) {
            SimpleComment simpleComment = new SimpleComment(comment);
            textFlowTarget.getExtensions(true).add(simpleComment);
        }
        doc.getTextFlowTargets().add(textFlowTarget);
    }

    /**
     * Reads properties from a given {@link InputStream} and adds them to the
     * given {@link Resource}.
     *
     * @param doc
     *            the resource to add properties textflows to
     * @param in
     *            the input stream to read the properties from
     * @throws IOException
     * @throws InvalidPropertiesFormatException
     */
    // TODO add documentation on exceptions thrown
    public void extractTemplate(Resource doc, InputStream in)
            throws IOException, RuntimeException {
        List resources = doc.getTextFlows();
        Properties props = loadProps(in);
        int nonTranslatableCount = 0;
        for (String key : props.stringPropertyNames()) {
            String comment = null;
            String rawComment = props.getRawComment(key);
            if (rawComment != null && rawComment.length() != 0) {
                StringBuilder sb = new StringBuilder(rawComment.length());
                nonTranslatableCount =
                        processCommentForNonTranslatable(nonTranslatableCount,
                                rawComment, sb);
                comment = sb.toString();
            }
            if (nonTranslatableCount == 0) {
                String content = props.getProperty(key);
                String id = getID(key, content);
                TextFlow textFlow = new TextFlow(id, sourceLocale, content);
                if (comment != null && comment.length() != 0) {
                    SimpleComment simpleComment = new SimpleComment(comment);
                    textFlow.getExtensions(true).add(simpleComment);
                }
                resources.add(textFlow);
            }
        }
    }

    /**
     * Processes a full comment for non-translatable sections, writing
     * translatable sections to a given string buffer.
     *
     * @param comment
     *            comment to process, may have multiple lines
     * @param sb
     *            string buffer to output comments in translatable blocks
     * @return adjusted non-translatable count, a value > 0 indicates that the
     *         current section is non-translatable
     * @throws InvalidPropertiesFormatException
     */
    private int processCommentForNonTranslatable(int nonTranslatableCount,
            String comment, StringBuilder sb)
            throws InvalidPropertiesFormatException {
        int nonTranslatable = nonTranslatableCount;
        String[] lines = comment.split(NEWLINE_REGEX);

        int lineNonTranslatable;
        for (String line : lines) {
            lineNonTranslatable = checkNonTranslatable(line);
            nonTranslatable += lineNonTranslatable;
            if (nonTranslatable < 0) {
                // TODO probably want a different exception here
                throw new InvalidPropertiesFormatException(
                        "Found '# END NON-TRANSLATABLE' "
                                + "without matching '# START NON-TRANSLATABLE'");
            }
            if (nonTranslatable == 0 && lineNonTranslatable == 0) {
                sb.append(Properties.cookCommentLine(line));
                // TODO if not last line
                sb.append('\n');
            }
        }

        return nonTranslatable;
    }

    /**
     * Checks a comment for START and END of NON-TRANSLATABLE sections within a
     * single line of a comment.
     *
     * @param line
     *            a single line of a comment
     * @return 0 if no NON-TRANSLATABLE comment is found, +1 for start, -1 for
     *         end
     */
    private int checkNonTranslatable(String line) {
        if (line.startsWith("# START NON-TRANSLATABLE")) {
            return 1;
        }
        if (line.startsWith("# END NON-TRANSLATABLE")) {
            return -1;
        }
        return 0;
    }

    private String getID(String key, String val) throws RuntimeException {
        if (key.length() > 255) {
            throw new RuntimeException(
                    "Zanata does not support property keys exceeding 255 characters");
        }
        return key;
    }

    private Properties loadProps(InputStream in) throws IOException,
            RuntimeException {
        Reader reader = new InputStreamReader(in, charset.getAlias());
        try {
            Properties props = new Properties();
            props.load(reader);
            return props;
        } finally {
            reader.close();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy