/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.cache; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.RuleViolation; /** * An analysis cache backed by a regular file. */ public class FileAnalysisCache extends AbstractAnalysisCache { private final File cacheFile; /** * Creates a new cache backed by the given file, and attempts to load pre-existing data from it. * @param cache The file on which to store analysis cache */ public FileAnalysisCache(final File cache) { super(); this.cacheFile = cache; loadFromFile(cache); } /** * Loads cache data from the given file. * @param cacheFile The file which backs the file analysis cache. */ private void loadFromFile(final File cacheFile) { if (cacheFile.exists()) { try ( DataInputStream inputStream = new DataInputStream( new BufferedInputStream(new FileInputStream(cacheFile))); ) { final String cacheVersion = inputStream.readUTF(); if (PMD.VERSION.equals(cacheVersion)) { // Cache seems valid, load the rest // Get checksums rulesetChecksum = inputStream.readLong(); classpathChecksum = inputStream.readLong(); // Cached results while (inputStream.available() > 0) { final String fileName = inputStream.readUTF(); final long checksum = inputStream.readLong(); final int countViolations = inputStream.readInt(); final List<RuleViolation> violations = new ArrayList<>(countViolations); for (int i = 0; i < countViolations; i++) { violations.add(CachedRuleViolation.loadFromStream(inputStream, fileName, ruleMapper)); } fileResultsCache.put(fileName, new AnalysisResult(checksum, violations)); } } else { LOG.info("Analysis cache invalidated, PMD version changed."); } } catch (final EOFException e) { LOG.warning("Cache file " + cacheFile.getPath() + " is malformed, will not be used for current analysis"); } catch (final IOException e) { LOG.severe("Could not load analysis cache to file. " + e.getMessage()); } } } @Override public void persist() { // Create directories missing along the way if (!cacheFile.exists()) { final File parentFile = cacheFile.getAbsoluteFile().getParentFile(); if (parentFile != null && !parentFile.exists()) { parentFile.mkdirs(); } } try ( DataOutputStream outputStream = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(cacheFile))); ) { outputStream.writeUTF(pmdVersion); outputStream.writeLong(rulesetChecksum); outputStream.writeLong(classpathChecksum); for (final Map.Entry<String, AnalysisResult> resultEntry : updatedResultsCache.entrySet()) { final List<RuleViolation> violations = resultEntry.getValue().getViolations(); outputStream.writeUTF(resultEntry.getKey()); outputStream.writeLong(resultEntry.getValue().getFileChecksum()); outputStream.writeInt(violations.size()); for (final RuleViolation rv : violations) { CachedRuleViolation.storeToStream(outputStream, rv); } } } catch (final IOException e) { LOG.severe("Could not persist analysis cache to file. " + e.getMessage()); } } }