/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 com.headwire.aem.tooling.intellij.console; import com.headwire.aem.tooling.intellij.config.ServerConfiguration; import com.headwire.aem.tooling.intellij.explorer.SlingServerTreeSelectionHandler; import com.headwire.aem.tooling.intellij.util.ComponentProvider; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Trinity; import com.intellij.openapi.wm.StatusBar; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.Topic; import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.WeakHashMap; /** * Created by Andreas Schaefer (Headwire.com) on 5/6/15. */ public class ConsoleLogModel implements Disposable { public static final Topic<Runnable> LOG_MODEL_CHANGED = Topic.create("LOG_MODEL_CHANGED", Runnable.class, Topic.BroadcastDirection.NONE); private final List<Notification> myNotifications = new ArrayList<Notification>(); private final Map<Notification, Long> myStamps = Collections.synchronizedMap(new WeakHashMap<Notification, Long>()); private final Map<Notification, String> myStatuses = Collections.synchronizedMap(new WeakHashMap<Notification, String>()); private Trinity<Notification, String, Long> myStatusMessage; private final Project myProject; final Map<Notification, Runnable> removeHandlers = new THashMap<Notification, Runnable>(); ConsoleLogModel(@Nullable Project project, @NotNull Disposable parentDisposable) { myProject = project; Disposer.register(parentDisposable, this); } void addNotification(Notification notification) { long stamp = System.currentTimeMillis(); if(myProject != null) { SlingServerTreeSelectionHandler selectionHandler = ComponentProvider.getComponent(myProject, SlingServerTreeSelectionHandler.class); if(selectionHandler != null) { ServerConfiguration serverConfiguration = selectionHandler.getCurrentConfiguration(); ServerConfiguration.LogFilter logFilter = serverConfiguration != null ? serverConfiguration.getLogFilter() : ServerConfiguration.LogFilter.info; switch(logFilter) { case debug: add(notification); break; case info: if(!(notification instanceof DebugNotification)) { add(notification); } break; case warning: if(notification.getType() != NotificationType.INFORMATION) { add(notification); } break; case error: default: if(notification.getType() == NotificationType.ERROR) { add(notification); } break; } } myStamps.put(notification, stamp); myStatuses.put(notification, ConsoleLog.formatForLog(notification, "").status); setStatusMessage(notification, stamp); fireModelChanged(); } } private void add(Notification notification) { synchronized (myNotifications) { myNotifications.add(notification); } } private static void fireModelChanged() { ApplicationManager.getApplication().getMessageBus().syncPublisher(LOG_MODEL_CHANGED).run(); } List<Notification> takeNotifications() { final ArrayList<Notification> result; synchronized (myNotifications) { result = getNotifications(); myNotifications.clear(); } fireModelChanged(); return result; } void setStatusMessage(@Nullable Notification statusMessage, long stamp) { synchronized (myNotifications) { if (myStatusMessage != null && myStatusMessage.first == statusMessage) return; if (myStatusMessage == null && statusMessage == null) return; myStatusMessage = statusMessage == null ? null : Trinity.create(statusMessage, myStatuses.get(statusMessage), stamp); } StatusBar.Info.set("", myProject, ConsoleLog.LOG_REQUESTOR); } @Nullable Trinity<Notification, String, Long> getStatusMessage() { synchronized (myNotifications) { return myStatusMessage; } } void logShown() { for (Notification notification : getNotifications()) { if (!notification.isImportant()) { removeNotification(notification); } } setStatusToImportant(); } public ArrayList<Notification> getNotifications() { synchronized (myNotifications) { return new ArrayList<Notification>(myNotifications); } } @Nullable public Long getNotificationTime(Notification notification) { return myStamps.get(notification); } public void removeNotification(Notification notification) { synchronized (myNotifications) { myNotifications.remove(notification); } Runnable handler = removeHandlers.remove(notification); if (handler != null) { UIUtil.invokeLaterIfNeeded(handler); } Trinity<Notification, String, Long> oldStatus = getStatusMessage(); if (oldStatus != null && notification == oldStatus.first) { setStatusToImportant(); } fireModelChanged(); } private void setStatusToImportant() { ArrayList<Notification> notifications = getNotifications(); Collections.reverse(notifications); Notification message = ContainerUtil.find(notifications, new Condition<Notification>() { @Override public boolean value(Notification notification) { return notification.isImportant(); } }); if (message == null) { setStatusMessage(null, 0); } else { Long notificationTime = getNotificationTime(message); assert notificationTime != null; setStatusMessage(message, notificationTime); } } public Project getProject() { //noinspection ConstantConditions return myProject; } @Override public void dispose() { } }