Java tutorial
/* * Copyright 2000-2009 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 com.intellij.psi.statistics.impl; import com.intellij.CommonBundle; import com.intellij.ide.IdeBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; import com.intellij.reference.SoftReference; import com.intellij.util.NotNullFunction; import com.intellij.util.ScrambledInputStream; import com.intellij.util.ScrambledOutputStream; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; import java.io.*; import java.util.Arrays; import java.util.HashSet; public class StatisticsManagerImpl extends StatisticsManager { private static final int UNIT_COUNT = 997; private static final Object LOCK = new Object(); @NonNls private static final String STORE_PATH = PathManager.getSystemPath() + File.separator + "stat"; private final SoftReference[] myUnits = new SoftReference[UNIT_COUNT]; private final HashSet<StatisticsUnit> myModifiedUnits = new HashSet<StatisticsUnit>(); private boolean myTestingStatistics; public int getUseCount(@NotNull final StatisticsInfo info) { if (info == StatisticsInfo.EMPTY) return 0; int useCount = 0; for (StatisticsInfo conjunct : info.getConjuncts()) { useCount = Math.max(doGetUseCount(conjunct), useCount); } return useCount; } private int doGetUseCount(StatisticsInfo info) { String key1 = info.getContext(); int unitNumber = getUnitNumber(key1); synchronized (LOCK) { StatisticsUnit unit = getUnit(unitNumber); return unit.getData(key1, info.getValue()); } } @Override public int getLastUseRecency(@NotNull StatisticsInfo info) { if (info == StatisticsInfo.EMPTY) return 0; int recency = Integer.MAX_VALUE; for (StatisticsInfo conjunct : info.getConjuncts()) { recency = Math.min(doGetRecency(conjunct), recency); } return recency; } private int doGetRecency(StatisticsInfo info) { String key1 = info.getContext(); int unitNumber = getUnitNumber(key1); synchronized (LOCK) { StatisticsUnit unit = getUnit(unitNumber); return unit.getRecency(key1, info.getValue()); } } public void incUseCount(@NotNull final StatisticsInfo info) { if (info == StatisticsInfo.EMPTY) return; if (ApplicationManager.getApplication().isUnitTestMode() && !myTestingStatistics) { return; } ApplicationManager.getApplication().assertIsDispatchThread(); for (StatisticsInfo conjunct : info.getConjuncts()) { doIncUseCount(conjunct); } } private void doIncUseCount(StatisticsInfo info) { final String key1 = info.getContext(); int unitNumber = getUnitNumber(key1); synchronized (LOCK) { StatisticsUnit unit = getUnit(unitNumber); unit.incData(key1, info.getValue()); myModifiedUnits.add(unit); } } public StatisticsInfo[] getAllValues(final String context) { final String[] strings; synchronized (LOCK) { strings = getUnit(getUnitNumber(context)).getKeys2(context); } return ContainerUtil.map2Array(strings, StatisticsInfo.class, new NotNullFunction<String, StatisticsInfo>() { @NotNull public StatisticsInfo fun(final String s) { return new StatisticsInfo(context, s); } }); } public void save() { synchronized (LOCK) { if (!ApplicationManager.getApplication().isUnitTestMode()) { ApplicationManager.getApplication().assertIsDispatchThread(); for (StatisticsUnit unit : myModifiedUnits) { saveUnit(unit.getNumber()); } } myModifiedUnits.clear(); } } private StatisticsUnit getUnit(int unitNumber) { SoftReference ref = myUnits[unitNumber]; if (ref != null) { StatisticsUnit unit = (StatisticsUnit) ref.get(); if (unit != null) return unit; } StatisticsUnit unit = loadUnit(unitNumber); if (unit == null) { unit = new StatisticsUnit(unitNumber); } myUnits[unitNumber] = new SoftReference<StatisticsUnit>(unit); return unit; } private static StatisticsUnit loadUnit(int unitNumber) { StatisticsUnit unit = new StatisticsUnit(unitNumber); if (!ApplicationManager.getApplication().isUnitTestMode()) { String path = getPathToUnit(unitNumber); try { InputStream in = new BufferedInputStream(new FileInputStream(path)); in = new ScrambledInputStream(in); try { unit.read(in); } finally { in.close(); } } catch (IOException e) { } catch (WrongFormatException e) { } } return unit; } private void saveUnit(int unitNumber) { if (!createStoreFolder()) return; StatisticsUnit unit = getUnit(unitNumber); String path = getPathToUnit(unitNumber); try { OutputStream out = new BufferedOutputStream(new FileOutputStream(path)); out = new ScrambledOutputStream(out); try { unit.write(out); } finally { out.close(); } } catch (IOException e) { Messages.showMessageDialog(IdeBundle.message("error.saving.statistics", e.getLocalizedMessage()), CommonBundle.getErrorTitle(), Messages.getErrorIcon()); } } private static int getUnitNumber(String key1) { return Math.abs(key1.hashCode()) % UNIT_COUNT; } private static boolean createStoreFolder() { File homeFile = new File(STORE_PATH); if (!homeFile.exists()) { if (!homeFile.mkdirs()) { Messages.showMessageDialog( IdeBundle.message("error.saving.statistic.failed.to.create.folder", STORE_PATH), CommonBundle.getErrorTitle(), Messages.getErrorIcon()); return false; } } return true; } @SuppressWarnings({ "HardCodedStringLiteral" }) private static String getPathToUnit(int unitNumber) { return STORE_PATH + File.separator + "unit." + unitNumber; } @TestOnly public void enableStatistics(@NotNull Disposable parentDisposable) { myTestingStatistics = true; Disposer.register(parentDisposable, new Disposable() { @Override public void dispose() { synchronized (LOCK) { Arrays.fill(myUnits, null); } myTestingStatistics = false; } }); } }