net.sf.logsupport.util.LogMessageUtil.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.logsupport.util.LogMessageUtil.java

Source

/*
 * Copyright 2010, Juergen Kellerer and other contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.logsupport.util;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import net.sf.logsupport.config.LogConfiguration;
import net.sf.logsupport.config.LogLevel;
import org.apache.commons.codec.binary.Hex;

import java.lang.ref.WeakReference;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Defines
 *
 * @author Juergen_Kellerer, 2010-04-28
 * @version 1.0
 */
public class LogMessageUtil {

    public static final String DELIMITED_ARTIFACT = "/";
    public static final String VARIABLE_ARTIFACT = "?";

    static final Charset UTF8 = Charset.forName("UTF-8");

    /**
     * Converts the given list of log expression to an message map.
     *
     * @param expressionList         The list of expression to convert.
     * @param mergeConstantExpressions Try to merge constant expression and create only one artifact where possible.
     * @return A map of log messages, where the unique log message is the key and multiple
     *         occurrences of the same message is the value.
     */
    public static Map<LogMessage, List<LogMessage>> toMessages(List<PsiMethodCallExpression> expressionList,
            boolean mergeConstantExpressions) {
        Map<LogMessage, List<LogMessage>> entries = new LinkedHashMap<LogMessage, List<LogMessage>>();

        for (PsiMethodCallExpression expression : expressionList) {
            LogMessage message = new LogMessage(expression, mergeConstantExpressions);
            if (entries.containsKey(message))
                entries.get(message).add(message);
            else
                entries.put(message, new ArrayList<LogMessage>(Arrays.asList(message)));
        }

        return entries;
    }

    /**
     * Converts the given method call expression to a log message instance.
     *
     * @param expression            the expression to convert.
     * @param mergeConstantExpressions Try to merge constant expression and create only one artifact where possible.
     * @return A new instance of LogMessage that is based on the given expression.
     */
    public static LogMessage toMessage(PsiMethodCallExpression expression, boolean mergeConstantExpressions) {
        return new LogMessage(expression, mergeConstantExpressions);
    }

    /**
     * Creates a new log message instance with the given values.
     *
     * @param id            The string ID of the message.
     * @param logLevel       The log level.
     * @param logId         The log ID.
     * @param messageArtifacts The message artifacts as string array.
     * @return A new instance of log message.
     */
    public static LogMessage newMessage(String id, String logLevel, String logId, String... messageArtifacts) {
        return new LogMessage(id, logLevel, logId, Arrays.asList(messageArtifacts));
    }

    /**
     * Describes a single log message.
     */
    public final static class LogMessage {

        private transient WeakReference<PsiMethodCallExpression> callExpression;

        private String source;
        private String id = "", logLevel = "", logId = "";
        private List<MessageArtifact> logMessage = new ArrayList<MessageArtifact>();

        private LogMessage(String id, String logLevel, String logId, List<String> messageArtifacts) {
            this.id = id;
            this.logLevel = logLevel;
            this.logId = logId;
            for (String s : messageArtifacts)
                logMessage.add(new MessageArtifact(s));
        }

        private LogMessage(PsiMethodCallExpression callExpression, boolean mergeConstantExpressions) {
            this.callExpression = new WeakReference<PsiMethodCallExpression>(callExpression);

            // Extract LogLevel
            LogLevel ll = LogPsiUtil.findLogLevel(callExpression);
            if (ll != null)
                logLevel = ll.name();

            PsiLiteralExpression literalExpression = LogPsiUtil
                    .findSupportedLiteralExpression(callExpression.getArgumentList());

            if (literalExpression != null) {
                // Extract ID
                LogConfiguration config = LogConfiguration.getInstance(callExpression.getContainingFile());
                if (config != null) {
                    NumericLogIdGenerator gen = config.getLogIdGenerator();
                    if (gen != null) {
                        String t = literalExpression.getText();
                        logId = t.length() > 2 ? gen.extractId(t.substring(1, t.length() - 1)) : "";
                    }
                }

                // Extract message artifacts
                PsiExpression pe = literalExpression;
                while (pe.getParent() instanceof PsiBinaryExpression)
                    pe = (PsiExpression) pe.getParent();
                MessageArtifact.build(pe, mergeConstantExpressions, logMessage);
            }

            try {
                StringBuilder b = new StringBuilder();
                b.append(logLevel).append(logId).append('-');
                for (MessageArtifact artifact : logMessage)
                    b.append(artifact);

                MessageDigest md = MessageDigest.getInstance("md5");
                id = new String(Hex.encodeHex(md.digest(b.toString().getBytes(UTF8))));
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
        }

        public String getSource() {
            if (source == null) {
                PsiMethodCallExpression callExpression = this.callExpression.get();
                VirtualFile sourceFile = callExpression == null ? null
                        : callExpression.getContainingFile().getVirtualFile();
                if (sourceFile != null) {
                    String source = sourceFile.getPresentableUrl();

                    ModuleManager moduleManager = ModuleManager.getInstance(callExpression.getProject());
                    for (Module module : moduleManager.getModules()) {
                        for (VirtualFile virtualFile : VirtualFileUtil.getSourceDirectories(module, true)) {
                            String moduleSourceURL = virtualFile.getPresentableUrl();
                            if (source.startsWith(moduleSourceURL)) {
                                source = "[" + module.getName() + "]: "
                                        + source.substring(moduleSourceURL.length() + 1).replace('\\', '/');
                                break;
                            }
                        }
                    }

                    Document document = callExpression.getContainingFile().getViewProvider().getDocument();
                    if (document != null)
                        source += ":" + document.getLineNumber(callExpression.getTextOffset());

                    this.source = source;
                } else
                    source = "";
            }
            return source;
        }

        public String getId() {
            return id;
        }

        public String getLogLevel() {
            return logLevel;
        }

        public String getLogId() {
            return logId;
        }

        public List<MessageArtifact> getLogMessage() {
            return Collections.unmodifiableList(logMessage);
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (MessageArtifact artifact : logMessage)
                b.append(artifact.toString());
            return b.toString();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (!(o instanceof LogMessage))
                return false;
            LogMessage message = (LogMessage) o;
            return id.equals(message.id);
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }
    }

    /**
     * Wraps a single log message artifact.
     */
    public final static class MessageArtifact {

        static void build(PsiExpression expression, boolean mergeConstantExpressions, List<MessageArtifact> out) {
            if (expression == null)
                return;
            if (expression instanceof PsiLiteralExpression)
                out.add(new MessageArtifact((PsiLiteralExpression) expression));
            else if (expression instanceof PsiBinaryExpression) {
                PsiBinaryExpression binaryExpression = (PsiBinaryExpression) expression;
                Object result = LogPsiUtil.computeConstantExpression(expression);
                boolean mergeable = result != null;

                if (mergeable && mergeConstantExpressions)
                    out.add(new MessageArtifact(result.toString()));
                else {
                    build(binaryExpression.getLOperand(), mergeConstantExpressions, out);
                    if (mergeable)
                        out.add(new MessageArtifact(DELIMITED_ARTIFACT));
                    build(binaryExpression.getROperand(), mergeConstantExpressions, out);
                }
            } else
                out.add(new MessageArtifact(VARIABLE_ARTIFACT));
        }

        private String constantValue = "";
        private PsiLiteralExpression value;

        private MessageArtifact(String constantValue) {
            this.constantValue = constantValue;
        }

        private MessageArtifact(PsiLiteralExpression expression) {
            String text = expression.getText();
            PsiType type = expression.getType();
            if (type != null && (type.equalsToText("java.lang.String") || type.equalsToText("char"))) {
                value = expression;
                int len = text.length();
                if (len > 2)
                    constantValue = text.substring(1, len - 1);
            } else
                constantValue = text;
        }

        public PsiLiteralExpression getValue() {
            return value;
        }

        public boolean isEditable() {
            return value != null;
        }

        @Override
        public String toString() {
            return constantValue;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (!(o instanceof MessageArtifact))
                return false;
            MessageArtifact that = (MessageArtifact) o;
            return !(constantValue != null ? !constantValue.equals(that.constantValue)
                    : that.constantValue != null);
        }

        @Override
        public int hashCode() {
            return constantValue != null ? constantValue.hashCode() : 0;
        }
    }

    LogMessageUtil() {
    }
}