/* * Copyright 2003-2015 JetBrains s.r.o. * * 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 jetbrains.mps.generator.impl; import jetbrains.mps.generator.IGeneratorLogger; import jetbrains.mps.messages.IMessage; import jetbrains.mps.messages.IMessageHandler; import jetbrains.mps.messages.Message; import jetbrains.mps.messages.MessageKind; import jetbrains.mps.util.containers.ConcurrentHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNodeReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Translates IGeneratorLogger calls into IMessageHandler's * Evgeny Gryaznov, Feb 23, 2010 */ public class GeneratorLoggerAdapter implements IGeneratorLogger { private static final int MAX_EXCEPTION_DEPTH = 10; protected final IMessageHandler myMessageHandler; protected final MessageFactory myFactory; protected final boolean myHandleInfo; protected final boolean myHandleWarnings; public GeneratorLoggerAdapter(IMessageHandler messageHandler, boolean handleInfo, boolean handleWarnings) { this(messageHandler, new BasicFactory(), handleInfo, handleWarnings); } public GeneratorLoggerAdapter(IMessageHandler messageHandler, MessageFactory msgFactory, boolean handleInfo, boolean handleWarnings) { myMessageHandler = messageHandler; myFactory = msgFactory; myHandleInfo = handleInfo; myHandleWarnings = handleWarnings; } @Override public boolean needsInfo() { return myHandleInfo; } @Override public boolean needsWarnings() { return myHandleWarnings; } @Override public void info(@Nullable SNodeReference node, @NotNull String message) { if (!myHandleInfo) { return; } report(MessageKind.INFORMATION, message, node); } @Override public void info(String message) { if (!myHandleInfo) { return; } report(MessageKind.INFORMATION, message, null); } public void trace(String message) { for (String s : message.split("\n")) { report(MessageKind.INFORMATION, s, null); } } @Override public void warning(String message) { if (!myHandleWarnings) { return; } warningReported(); report(MessageKind.WARNING, message, null); } @Override public void warning(@Nullable SNodeReference node, @NotNull String message, @Nullable ProblemDescription... descriptions) { if (!myHandleWarnings) { return; } warningReported(); report(MessageKind.WARNING, message, node, descriptions); } @Override public void error(@Nullable SNodeReference node, @NotNull String message, @Nullable ProblemDescription... descriptions) { errorReported(); report(MessageKind.ERROR, message, node, descriptions); } @Override public void error(String message) { errorReported(); report(MessageKind.ERROR, message, null); } @Override public void handleException(Throwable t) { String text = t.getMessage(); Throwable cause = t; for (int i = 0; i < MAX_EXCEPTION_DEPTH; ++i) { if (text != null || cause == null) { break; } text = cause.getMessage(); cause = cause.getCause(); } if (text == null) { text = String.format("An exception was encountered: %s (no message)", t.getClass().getName()); } else { text = String.format("(%s): %s", t.getClass().getName(), text); } errorReported(); addMessage(new Message(MessageKind.ERROR, text).setException(t)); } protected void errorReported() { // no-op } protected void warningReported() { // no-op } protected final void report(MessageKind kind, String text, SNodeReference node) { addMessage(myFactory.prepare(kind, text == null ? "" : text, node)); } protected final void addMessage(@NotNull IMessage msg) { myMessageHandler.handle(msg); } protected final void report(MessageKind kind, String text, SNodeReference node, ProblemDescription... descriptions) { if (descriptions == null) { report(kind, text, node); return; } List<Message> messages = new ArrayList<Message>(descriptions.length + 1); messages.add(myFactory.prepare(kind, text, node)); for (ProblemDescription d : descriptions) { if (d != null) { messages.add(myFactory.prepare(kind, "-- " + d.getMessage(), d.getNode())); } } for (Message m : messages) { addMessage(m); } } interface MessageFactory { Message prepare(@NotNull MessageKind kind, @NotNull String text, @Nullable SNodeReference node); } static class BasicFactory implements MessageFactory { @NotNull public Message prepare(@NotNull MessageKind kind, @NotNull String text, @Nullable SNodeReference node) { Message message = new Message(kind, text); message.setHintObject(node); return message; } } /** * Concurrent record of models reported through messages */ static class RecordingFactory implements MessageFactory { @SuppressWarnings("unchecked") private final Collection<SModelReference>[] a = new Collection[MessageKind.values().length]; private final MessageFactory myDelegate; public RecordingFactory(@NotNull MessageFactory delegate) { myDelegate = delegate; for (MessageKind k : MessageKind.values()) { a[k.ordinal()] = new ConcurrentHashSet<SModelReference>(); } } public Collection<SModelReference> ofKind(MessageKind kind) { return a[kind.ordinal()]; } public void reset() { for (MessageKind k : MessageKind.values()) { a[k.ordinal()].clear(); } } /** * Record additional access to model, for use from log4j listeners */ public void record(@NotNull MessageKind kind, @Nullable SModelReference modelRef) { if (modelRef != null) { a[kind.ordinal()].add(modelRef); } } @NotNull @Override public Message prepare(@NotNull MessageKind kind, @NotNull String text, @Nullable SNodeReference node) { if (node != null) { record(kind, node.getModelReference()); } return myDelegate.prepare(kind, text, node); } } }