From d84014d6f9043632961940d292989d58e2a6e73f Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Mon, 18 Apr 2011 16:04:44 +0200 Subject: [PATCH] [SONAR-1957] Add ViolationQuery on SonarIndex and DecoratorContext APIs --- .../sonar/batch/DefaultDecoratorContext.java | 339 +++--- .../org/sonar/batch/index/DefaultIndex.java | 1082 +++++++++-------- .../sonar/batch/index/DefaultIndexTest.java | 426 ++++--- .../org/sonar/api/batch/DecoratorContext.java | 305 ++--- .../java/org/sonar/api/batch/SonarIndex.java | 344 +++--- .../sonar/api/violations/ViolationQuery.java | 89 ++ 6 files changed, 1392 insertions(+), 1193 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/violations/ViolationQuery.java diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java index fde3c9099d4..4ca401c310a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java @@ -1,164 +1,175 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.batch; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Set; - -import com.google.common.collect.Lists; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.Event; -import org.sonar.api.batch.SonarIndex; -import org.sonar.api.design.Dependency; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.MeasuresFilters; -import org.sonar.api.measures.Metric; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rules.Violation; - -public class DefaultDecoratorContext implements DecoratorContext { - - private SonarIndex index; - private Resource resource; - private boolean readOnly = false; - - private List childrenContexts; - - public DefaultDecoratorContext(Resource resource, - SonarIndex index, - List childrenContexts) { - this.index = index; - this.resource = resource; - this.childrenContexts = childrenContexts; - } - - public DefaultDecoratorContext setReadOnly(boolean b) { - readOnly = b; - childrenContexts = null; - return this; - } - - public Project getProject() { - return index.getProject(); - } - - public List getChildren() { - checkReadOnly("getModules"); - return childrenContexts; - } - - private void checkReadOnly(String methodName) { - if (readOnly) { - throw new IllegalStateException("Method DecoratorContext." + methodName + "() can not be executed on children."); - } - } - - public M getMeasures(MeasuresFilter filter) { - return index.getMeasures(resource, filter); - } - - public Measure getMeasure(Metric metric) { - return index.getMeasure(resource, metric); - } - - public Collection getChildrenMeasures(MeasuresFilter filter) { - List result = Lists.newArrayList(); - for (DecoratorContext childContext : childrenContexts) { - Object childResult = childContext.getMeasures(filter); - if (childResult != null) { - if (childResult instanceof Collection) { - result.addAll((Collection) childResult); - } else { - result.add((Measure) childResult); - } - } - } - return result; - } - - public Collection getChildrenMeasures(Metric metric) { - return getChildrenMeasures(MeasuresFilters.metric(metric)); - } - - public Resource getResource() { - return resource; - } - - public DecoratorContext saveMeasure(Measure measure) { - checkReadOnly("saveMeasure"); - index.addMeasure(resource, measure); - return this; - } - - public DecoratorContext saveMeasure(Metric metric, Double value) { - checkReadOnly("saveMeasure"); - index.addMeasure(resource, new Measure(metric, value)); - return this; - } - - public List getViolations() { - return index.getViolations(resource); - } - - public Dependency saveDependency(Dependency dependency) { - checkReadOnly("addDependency"); - return index.addDependency(dependency); - } - - public Set getDependencies() { - return index.getDependencies(); - } - - public Collection getIncomingDependencies() { - return index.getIncomingEdges(resource); - } - - public Collection getOutgoingDependencies() { - return index.getOutgoingEdges(resource); - } - - public List getEvents() { - return index.getEvents(resource); - } - - public Event createEvent(String name, String description, String category, Date date) { - return index.addEvent(resource, name, description, category, date); - } - - public void deleteEvent(Event event) { - index.deleteEvent(event); - } - - public DefaultDecoratorContext saveViolation(Violation violation, boolean force) { - if (violation.getResource() == null) { - violation.setResource(resource); - } - index.addViolation(violation, force); - return this; - } - - public DefaultDecoratorContext saveViolation(Violation violation) { - return saveViolation(violation, false); - } -} +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.Lists; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.Event; +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Violation; +import org.sonar.api.violations.ViolationQuery; + +public class DefaultDecoratorContext implements DecoratorContext { + + private SonarIndex index; + private Resource resource; + private boolean readOnly = false; + + private List childrenContexts; + + public DefaultDecoratorContext(Resource resource, + SonarIndex index, + List childrenContexts) { + this.index = index; + this.resource = resource; + this.childrenContexts = childrenContexts; + } + + public DefaultDecoratorContext setReadOnly(boolean b) { + readOnly = b; + childrenContexts = null; + return this; + } + + public Project getProject() { + return index.getProject(); + } + + public List getChildren() { + checkReadOnly("getModules"); + return childrenContexts; + } + + private void checkReadOnly(String methodName) { + if (readOnly) { + throw new IllegalStateException("Method DecoratorContext." + methodName + "() can not be executed on children."); + } + } + + public M getMeasures(MeasuresFilter filter) { + return index.getMeasures(resource, filter); + } + + public Measure getMeasure(Metric metric) { + return index.getMeasure(resource, metric); + } + + public Collection getChildrenMeasures(MeasuresFilter filter) { + List result = Lists.newArrayList(); + for (DecoratorContext childContext : childrenContexts) { + Object childResult = childContext.getMeasures(filter); + if (childResult != null) { + if (childResult instanceof Collection) { + result.addAll((Collection) childResult); + } else { + result.add((Measure) childResult); + } + } + } + return result; + } + + public Collection getChildrenMeasures(Metric metric) { + return getChildrenMeasures(MeasuresFilters.metric(metric)); + } + + public Resource getResource() { + return resource; + } + + public DecoratorContext saveMeasure(Measure measure) { + checkReadOnly("saveMeasure"); + index.addMeasure(resource, measure); + return this; + } + + public DecoratorContext saveMeasure(Metric metric, Double value) { + checkReadOnly("saveMeasure"); + index.addMeasure(resource, new Measure(metric, value)); + return this; + } + + /** + * {@inheritDoc} + */ + public List getViolations(ViolationQuery violationQuery) { + return index.getViolations(violationQuery); + } + + /** + * {@inheritDoc} + */ + public List getViolations() { + return index.getViolations(resource); + } + + public Dependency saveDependency(Dependency dependency) { + checkReadOnly("addDependency"); + return index.addDependency(dependency); + } + + public Set getDependencies() { + return index.getDependencies(); + } + + public Collection getIncomingDependencies() { + return index.getIncomingEdges(resource); + } + + public Collection getOutgoingDependencies() { + return index.getOutgoingEdges(resource); + } + + public List getEvents() { + return index.getEvents(resource); + } + + public Event createEvent(String name, String description, String category, Date date) { + return index.addEvent(resource, name, description, category, date); + } + + public void deleteEvent(Event event) { + index.deleteEvent(event); + } + + public DefaultDecoratorContext saveViolation(Violation violation, boolean force) { + if (violation.getResource() == null) { + violation.setResource(resource); + } + index.addViolation(violation, force); + return this; + } + + public DefaultDecoratorContext saveViolation(Violation violation) { + return saveViolation(violation, false); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index 484ab7ce54f..b8f42c96040 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -1,533 +1,549 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.batch.index; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.Event; -import org.sonar.api.batch.SonarIndex; -import org.sonar.api.database.model.ResourceModel; -import org.sonar.api.design.Dependency; -import org.sonar.api.measures.*; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.*; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.Violation; -import org.sonar.api.utils.SonarException; -import org.sonar.batch.DefaultResourceCreationLock; -import org.sonar.batch.ProjectTree; -import org.sonar.batch.ResourceFilters; -import org.sonar.batch.ViolationFilters; - -import java.util.*; - -public class DefaultIndex extends SonarIndex { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultIndex.class); - - private RulesProfile profile; - private PersistenceManager persistence; - private DefaultResourceCreationLock lock; - private MetricFinder metricFinder; - - // filters - private ViolationFilters violationFilters; - private ResourceFilters resourceFilters; - - // caches - private Project currentProject; - private Map buckets = Maps.newHashMap(); - private Set dependencies = Sets.newHashSet(); - private Map> outgoingDependenciesByResource = Maps.newHashMap(); - private Map> incomingDependenciesByResource = Maps.newHashMap(); - private ProjectTree projectTree; - - public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder) { - this.persistence = persistence; - this.lock = lock; - this.projectTree = projectTree; - this.metricFinder = metricFinder; - } - - public void start() { - Project rootProject = projectTree.getRootProject(); - doStart(rootProject); - } - - void doStart(Project rootProject) { - Bucket bucket = new Bucket(rootProject); - buckets.put(rootProject, bucket); - persistence.saveProject(rootProject, null); - currentProject = rootProject; - - for (Project project : rootProject.getModules()) { - addProject(project); - } - } - - private void addProject(Project project) { - addResource(project); - for (Project module : project.getModules()) { - addProject(module); - } - } - - public Project getProject() { - return currentProject; - } - - public void setCurrentProject(Project project, ResourceFilters resourceFilters, ViolationFilters violationFilters, RulesProfile profile) { - this.currentProject = project; - - // the following components depend on the current project, so they need to be reloaded. - this.resourceFilters = resourceFilters; - this.violationFilters = violationFilters; - this.profile = profile; - } - - /** - * Keep only project stuff - */ - public void clear() { - Iterator> it = buckets.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - Resource resource = entry.getKey(); - if (!ResourceUtils.isSet(resource)) { - entry.getValue().clear(); - it.remove(); - } - } - - Set projectDependencies = getDependenciesBetweenProjects(); - dependencies.clear(); - incomingDependenciesByResource.clear(); - outgoingDependenciesByResource.clear(); - for (Dependency projectDependency : projectDependencies) { - projectDependency.setId(null); - registerDependency(projectDependency); - } - - lock.unlock(); - } - - public Measure getMeasure(Resource resource, Metric metric) { - Bucket bucket = buckets.get(resource); - if (bucket != null) { - Measure measure = bucket.getMeasures(MeasuresFilters.metric(metric)); - if (measure != null) { - return persistence.reloadMeasure(measure); - } - } - return null; - } - - public M getMeasures(Resource resource, MeasuresFilter filter) { - Bucket bucket = buckets.get(resource); - if (bucket != null) { - // TODO the data measures which are not kept in memory are not reloaded yet. Use getMeasure(). - return bucket.getMeasures(filter); - } - return null; - } - - /** - * the measure is updated if it's already registered. - */ - public Measure addMeasure(Resource resource, Measure measure) { - Bucket bucket = checkIndexed(resource); - if (bucket != null && !bucket.isExcluded()) { - Metric metric = metricFinder.findByKey(measure.getMetricKey()); - if (metric == null) { - throw new SonarException("Unknown metric: " + measure.getMetricKey()); - } - measure.setMetric(metric); - bucket.addMeasure(measure); - - if (measure.getPersistenceMode().useDatabase()) { - persistence.saveMeasure(resource, measure); - } - } - return measure; - } - - // - // - // - // DEPENDENCIES - // - // - // - - public Dependency addDependency(Dependency dependency) { - Dependency existingDep = getEdge(dependency.getFrom(), dependency.getTo()); - if (existingDep != null) { - return existingDep; - } - - Dependency parentDependency = dependency.getParent(); - if (parentDependency != null) { - addDependency(parentDependency); - } - - if (registerDependency(dependency)) { - persistence.saveDependency(currentProject, dependency, parentDependency); - } - return dependency; - } - - boolean registerDependency(Dependency dependency) { - Bucket fromBucket = doIndex(dependency.getFrom()); - Bucket toBucket = doIndex(dependency.getTo()); - - if (fromBucket != null && !fromBucket.isExcluded() && toBucket != null && !toBucket.isExcluded()) { - dependencies.add(dependency); - registerOutgoingDependency(dependency); - registerIncomingDependency(dependency); - return true; - } - return false; - } - - private void registerOutgoingDependency(Dependency dependency) { - Map outgoingDeps = outgoingDependenciesByResource.get(dependency.getFrom()); - if (outgoingDeps == null) { - outgoingDeps = new HashMap(); - outgoingDependenciesByResource.put(dependency.getFrom(), outgoingDeps); - } - outgoingDeps.put(dependency.getTo(), dependency); - } - - private void registerIncomingDependency(Dependency dependency) { - Map incomingDeps = incomingDependenciesByResource.get(dependency.getTo()); - if (incomingDeps == null) { - incomingDeps = new HashMap(); - incomingDependenciesByResource.put(dependency.getTo(), incomingDeps); - } - incomingDeps.put(dependency.getFrom(), dependency); - } - - public Set getDependencies() { - return dependencies; - } - - public Dependency getEdge(Resource from, Resource to) { - Map map = outgoingDependenciesByResource.get(from); - if (map != null) { - return map.get(to); - } - return null; - } - - public boolean hasEdge(Resource from, Resource to) { - return getEdge(from, to) != null; - } - - public Set getVertices() { - return buckets.keySet(); - } - - public Collection getOutgoingEdges(Resource from) { - Map deps = outgoingDependenciesByResource.get(from); - if (deps != null) { - return deps.values(); - } - return Collections.emptyList(); - } - - public Collection getIncomingEdges(Resource to) { - Map deps = incomingDependenciesByResource.get(to); - if (deps != null) { - return deps.values(); - } - return Collections.emptyList(); - } - - Set getDependenciesBetweenProjects() { - Set result = Sets.newLinkedHashSet(); - for (Dependency dependency : dependencies) { - if (ResourceUtils.isSet(dependency.getFrom()) || ResourceUtils.isSet(dependency.getTo())) { - result.add(dependency); - } - } - return result; - } - - // - // - // - // VIOLATIONS - // - // - // - - public List getViolations(Resource resource) { - Bucket bucket = buckets.get(resource); - if (bucket == null) { - return Collections.emptyList(); - } - return bucket.getViolations(); - } - - public void addViolation(Violation violation, boolean force) { - Resource resource = violation.getResource(); - if (resource == null) { - violation.setResource(currentProject); - } else if (!Scopes.isHigherThanOrEquals(resource, Scopes.FILE)) { - throw new IllegalArgumentException("Violations are only supported on files, directories and project"); - } - - if (violation.getRule() == null) { - LOG.warn("Rule is null, ignoring violation {}", violation); - return; - } - - Bucket bucket = checkIndexed(resource); - if (bucket != null && !bucket.isExcluded()) { - boolean isIgnored = !force && violationFilters != null && violationFilters.isIgnored(violation); - if (!isIgnored) { - ActiveRule activeRule = profile.getActiveRule(violation.getRule()); - if (activeRule == null) { - if (currentProject.getReuseExistingRulesConfig()) { - violation.setSeverity(violation.getRule().getSeverity()); - doAddViolation(violation, bucket); - - } else { - LoggerFactory.getLogger(getClass()).debug("Rule is not activated, ignoring violation {}", violation); - } - - } else { - violation.setSeverity(activeRule.getSeverity()); - doAddViolation(violation, bucket); - } - } - } - } - - private void doAddViolation(Violation violation, Bucket bucket) { - bucket.addViolation(violation); - } - - // - // - // - // LINKS - // - // - // - - public void addLink(ProjectLink link) { - persistence.saveLink(currentProject, link); - } - - public void deleteLink(String key) { - persistence.deleteLink(currentProject, key); - } - - // - // - // - // EVENTS - // - // - // - - public List getEvents(Resource resource) { - // currently events are not cached in memory - return persistence.getEvents(resource); - } - - public void deleteEvent(Event event) { - persistence.deleteEvent(event); - } - - public Event addEvent(Resource resource, String name, String description, String category, Date date) { - Event event = new Event(name, description, category); - event.setDate(date); - persistence.saveEvent(resource, event); - return null; - } - - public void setSource(Resource reference, String source) { - Bucket bucket = checkIndexed(reference); - if (bucket != null && !bucket.isExcluded()) { - persistence.setSource(reference, source); - } - } - - /** - * Does nothing if the resource is already registered. - */ - public Resource addResource(Resource resource) { - Bucket bucket = doIndex(resource); - return bucket != null ? bucket.getResource() : null; - } - - public R getResource(R reference) { - Bucket bucket = buckets.get(reference); - if (bucket != null) { - return (R) bucket.getResource(); - } - return null; - } - - static String createUID(Project project, Resource resource) { - String uid = resource.getKey(); - if (!StringUtils.equals(Scopes.PROJECT, resource.getScope())) { - // not a project nor a library - uid = new StringBuilder(ResourceModel.KEY_SIZE) - .append(project.getKey()) - .append(':') - .append(resource.getKey()) - .toString(); - } - return uid; - } - - private boolean checkExclusion(Resource resource, Bucket parent) { - boolean excluded = (parent != null && parent.isExcluded()) || (resourceFilters != null && resourceFilters.isExcluded(resource)); - resource.setExcluded(excluded); - return excluded; - } - - public List getChildren(Resource resource) { - return getChildren(resource, false); - } - - public List getChildren(Resource resource, boolean acceptExcluded) { - List children = Lists.newLinkedList(); - Bucket bucket = getBucket(resource, acceptExcluded); - if (bucket != null) { - for (Bucket childBucket : bucket.getChildren()) { - if (acceptExcluded || !childBucket.isExcluded()) { - children.add(childBucket.getResource()); - } - } - } - return children; - } - - public Resource getParent(Resource resource) { - Bucket bucket = getBucket(resource, false); - if (bucket != null && bucket.getParent() != null) { - return bucket.getParent().getResource(); - } - return null; - } - - public boolean index(Resource resource) { - Bucket bucket = doIndex(resource); - return bucket != null && !bucket.isExcluded(); - } - - private Bucket doIndex(Resource resource) { - if (resource.getParent() != null) { - doIndex(resource.getParent()); - } - return doIndex(resource, resource.getParent()); - } - - public boolean index(Resource resource, Resource parentReference) { - Bucket bucket = doIndex(resource, parentReference); - return bucket != null && !bucket.isExcluded(); - } - - private Bucket doIndex(Resource resource, Resource parentReference) { - Bucket bucket = buckets.get(resource); - if (bucket != null) { - return bucket; - } - - checkLock(resource); - - Resource parent = null; - if (!ResourceUtils.isLibrary(resource)) { - // a library has no parent - parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject); - } - - Bucket parentBucket = getBucket(parent, true); - if (parentBucket == null && parent != null) { - LOG.warn("Resource ignored, parent is not indexed: " + resource); - return null; - } - - resource.setEffectiveKey(createUID(currentProject, resource)); - bucket = new Bucket(resource).setParent(parentBucket); - buckets.put(resource, bucket); - - boolean excluded = checkExclusion(resource, parentBucket); - if (!excluded) { - persistence.saveResource(currentProject, resource, (parentBucket != null ? parentBucket.getResource() : null)); - } - return bucket; - } - - private void checkLock(Resource resource) { - if (lock.isLocked() && !ResourceUtils.isLibrary(resource)) { - if (lock.isFailWhenLocked()) { - throw new SonarException("Index is locked, resource can not be indexed: " + resource); - } - } - } - - private Bucket checkIndexed(Resource resource) { - Bucket bucket = getBucket(resource, true); - if (bucket == null) { - if (lock.isLocked()) { - if (lock.isFailWhenLocked()) { - throw new ResourceNotIndexedException(resource); - } - LOG.warn("Resource will be ignored in next Sonar versions, index is locked: " + resource); - } - if (Scopes.isDirectory(resource) || Scopes.isFile(resource)) { - bucket = doIndex(resource); - } else if (!lock.isLocked()) { - LOG.warn("Resource will be ignored in next Sonar versions, it must be indexed before adding data: " + resource); - } - } - return bucket; - } - - public boolean isExcluded(Resource reference) { - Bucket bucket = getBucket(reference, true); - return bucket != null && bucket.isExcluded(); - } - - public boolean isIndexed(Resource reference, boolean acceptExcluded) { - return getBucket(reference, acceptExcluded) != null; - } - - private Bucket getBucket(Resource resource, boolean acceptExcluded) { - Bucket bucket = null; - if (resource != null) { - bucket = buckets.get(resource); - if (!acceptExcluded && bucket != null && bucket.isExcluded()) { - bucket = null; - } - } - return bucket; - } -} +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.index; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Event; +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.*; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.*; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Violation; +import org.sonar.api.utils.SonarException; +import org.sonar.api.violations.ViolationQuery; +import org.sonar.batch.DefaultResourceCreationLock; +import org.sonar.batch.ProjectTree; +import org.sonar.batch.ResourceFilters; +import org.sonar.batch.ViolationFilters; + +import java.util.*; + +public class DefaultIndex extends SonarIndex { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultIndex.class); + + private RulesProfile profile; + private PersistenceManager persistence; + private DefaultResourceCreationLock lock; + private MetricFinder metricFinder; + + // filters + private ViolationFilters violationFilters; + private ResourceFilters resourceFilters; + + // caches + private Project currentProject; + private Map buckets = Maps.newHashMap(); + private Set dependencies = Sets.newHashSet(); + private Map> outgoingDependenciesByResource = Maps.newHashMap(); + private Map> incomingDependenciesByResource = Maps.newHashMap(); + private ProjectTree projectTree; + + public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder) { + this.persistence = persistence; + this.lock = lock; + this.projectTree = projectTree; + this.metricFinder = metricFinder; + } + + public void start() { + Project rootProject = projectTree.getRootProject(); + doStart(rootProject); + } + + void doStart(Project rootProject) { + Bucket bucket = new Bucket(rootProject); + buckets.put(rootProject, bucket); + persistence.saveProject(rootProject, null); + currentProject = rootProject; + + for (Project project : rootProject.getModules()) { + addProject(project); + } + } + + private void addProject(Project project) { + addResource(project); + for (Project module : project.getModules()) { + addProject(module); + } + } + + public Project getProject() { + return currentProject; + } + + public void setCurrentProject(Project project, ResourceFilters resourceFilters, ViolationFilters violationFilters, RulesProfile profile) { + this.currentProject = project; + + // the following components depend on the current project, so they need to be reloaded. + this.resourceFilters = resourceFilters; + this.violationFilters = violationFilters; + this.profile = profile; + } + + /** + * Keep only project stuff + */ + public void clear() { + Iterator> it = buckets.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + Resource resource = entry.getKey(); + if (!ResourceUtils.isSet(resource)) { + entry.getValue().clear(); + it.remove(); + } + } + + Set projectDependencies = getDependenciesBetweenProjects(); + dependencies.clear(); + incomingDependenciesByResource.clear(); + outgoingDependenciesByResource.clear(); + for (Dependency projectDependency : projectDependencies) { + projectDependency.setId(null); + registerDependency(projectDependency); + } + + lock.unlock(); + } + + public Measure getMeasure(Resource resource, Metric metric) { + Bucket bucket = buckets.get(resource); + if (bucket != null) { + Measure measure = bucket.getMeasures(MeasuresFilters.metric(metric)); + if (measure != null) { + return persistence.reloadMeasure(measure); + } + } + return null; + } + + public M getMeasures(Resource resource, MeasuresFilter filter) { + Bucket bucket = buckets.get(resource); + if (bucket != null) { + // TODO the data measures which are not kept in memory are not reloaded yet. Use getMeasure(). + return bucket.getMeasures(filter); + } + return null; + } + + /** + * the measure is updated if it's already registered. + */ + public Measure addMeasure(Resource resource, Measure measure) { + Bucket bucket = checkIndexed(resource); + if (bucket != null && !bucket.isExcluded()) { + Metric metric = metricFinder.findByKey(measure.getMetricKey()); + if (metric == null) { + throw new SonarException("Unknown metric: " + measure.getMetricKey()); + } + measure.setMetric(metric); + bucket.addMeasure(measure); + + if (measure.getPersistenceMode().useDatabase()) { + persistence.saveMeasure(resource, measure); + } + } + return measure; + } + + // + // + // + // DEPENDENCIES + // + // + // + + public Dependency addDependency(Dependency dependency) { + Dependency existingDep = getEdge(dependency.getFrom(), dependency.getTo()); + if (existingDep != null) { + return existingDep; + } + + Dependency parentDependency = dependency.getParent(); + if (parentDependency != null) { + addDependency(parentDependency); + } + + if (registerDependency(dependency)) { + persistence.saveDependency(currentProject, dependency, parentDependency); + } + return dependency; + } + + boolean registerDependency(Dependency dependency) { + Bucket fromBucket = doIndex(dependency.getFrom()); + Bucket toBucket = doIndex(dependency.getTo()); + + if (fromBucket != null && !fromBucket.isExcluded() && toBucket != null && !toBucket.isExcluded()) { + dependencies.add(dependency); + registerOutgoingDependency(dependency); + registerIncomingDependency(dependency); + return true; + } + return false; + } + + private void registerOutgoingDependency(Dependency dependency) { + Map outgoingDeps = outgoingDependenciesByResource.get(dependency.getFrom()); + if (outgoingDeps == null) { + outgoingDeps = new HashMap(); + outgoingDependenciesByResource.put(dependency.getFrom(), outgoingDeps); + } + outgoingDeps.put(dependency.getTo(), dependency); + } + + private void registerIncomingDependency(Dependency dependency) { + Map incomingDeps = incomingDependenciesByResource.get(dependency.getTo()); + if (incomingDeps == null) { + incomingDeps = new HashMap(); + incomingDependenciesByResource.put(dependency.getTo(), incomingDeps); + } + incomingDeps.put(dependency.getFrom(), dependency); + } + + public Set getDependencies() { + return dependencies; + } + + public Dependency getEdge(Resource from, Resource to) { + Map map = outgoingDependenciesByResource.get(from); + if (map != null) { + return map.get(to); + } + return null; + } + + public boolean hasEdge(Resource from, Resource to) { + return getEdge(from, to) != null; + } + + public Set getVertices() { + return buckets.keySet(); + } + + public Collection getOutgoingEdges(Resource from) { + Map deps = outgoingDependenciesByResource.get(from); + if (deps != null) { + return deps.values(); + } + return Collections.emptyList(); + } + + public Collection getIncomingEdges(Resource to) { + Map deps = incomingDependenciesByResource.get(to); + if (deps != null) { + return deps.values(); + } + return Collections.emptyList(); + } + + Set getDependenciesBetweenProjects() { + Set result = Sets.newLinkedHashSet(); + for (Dependency dependency : dependencies) { + if (ResourceUtils.isSet(dependency.getFrom()) || ResourceUtils.isSet(dependency.getTo())) { + result.add(dependency); + } + } + return result; + } + + // + // + // + // VIOLATIONS + // + // + // + + /** + * {@inheritDoc} + */ + public List getViolations(ViolationQuery violationQuery) { + Resource resource = violationQuery.getResource(); + if (resource == null) { + throw new IllegalArgumentException("A resource must be set on the ViolationQuery in order to search for violations."); + } + Bucket bucket = buckets.get(resource); + if (bucket == null) { + return Collections.emptyList(); + } + List filteredViolations = Lists.newArrayList(); + boolean ignoreSwitchedOff = violationQuery.ignoreSwitchedOff(); + for (Violation violation : bucket.getViolations()) { + if ( ignoreSwitchedOff && violation.isSwitchedOff()) { + continue; + } + filteredViolations.add(violation); + } + return filteredViolations; + } + + public void addViolation(Violation violation, boolean force) { + Resource resource = violation.getResource(); + if (resource == null) { + violation.setResource(currentProject); + } else if (!Scopes.isHigherThanOrEquals(resource, Scopes.FILE)) { + throw new IllegalArgumentException("Violations are only supported on files, directories and project"); + } + + if (violation.getRule() == null) { + LOG.warn("Rule is null, ignoring violation {}", violation); + return; + } + + Bucket bucket = checkIndexed(resource); + if (bucket != null && !bucket.isExcluded()) { + boolean isIgnored = !force && violationFilters != null && violationFilters.isIgnored(violation); + if (!isIgnored) { + ActiveRule activeRule = profile.getActiveRule(violation.getRule()); + if (activeRule == null) { + if (currentProject.getReuseExistingRulesConfig()) { + violation.setSeverity(violation.getRule().getSeverity()); + doAddViolation(violation, bucket); + + } else { + LoggerFactory.getLogger(getClass()).debug("Rule is not activated, ignoring violation {}", violation); + } + + } else { + violation.setSeverity(activeRule.getSeverity()); + doAddViolation(violation, bucket); + } + } + } + } + + private void doAddViolation(Violation violation, Bucket bucket) { + bucket.addViolation(violation); + } + + // + // + // + // LINKS + // + // + // + + public void addLink(ProjectLink link) { + persistence.saveLink(currentProject, link); + } + + public void deleteLink(String key) { + persistence.deleteLink(currentProject, key); + } + + // + // + // + // EVENTS + // + // + // + + public List getEvents(Resource resource) { + // currently events are not cached in memory + return persistence.getEvents(resource); + } + + public void deleteEvent(Event event) { + persistence.deleteEvent(event); + } + + public Event addEvent(Resource resource, String name, String description, String category, Date date) { + Event event = new Event(name, description, category); + event.setDate(date); + persistence.saveEvent(resource, event); + return null; + } + + public void setSource(Resource reference, String source) { + Bucket bucket = checkIndexed(reference); + if (bucket != null && !bucket.isExcluded()) { + persistence.setSource(reference, source); + } + } + + /** + * Does nothing if the resource is already registered. + */ + public Resource addResource(Resource resource) { + Bucket bucket = doIndex(resource); + return bucket != null ? bucket.getResource() : null; + } + + public R getResource(R reference) { + Bucket bucket = buckets.get(reference); + if (bucket != null) { + return (R) bucket.getResource(); + } + return null; + } + + static String createUID(Project project, Resource resource) { + String uid = resource.getKey(); + if (!StringUtils.equals(Scopes.PROJECT, resource.getScope())) { + // not a project nor a library + uid = new StringBuilder(ResourceModel.KEY_SIZE) + .append(project.getKey()) + .append(':') + .append(resource.getKey()) + .toString(); + } + return uid; + } + + private boolean checkExclusion(Resource resource, Bucket parent) { + boolean excluded = (parent != null && parent.isExcluded()) || (resourceFilters != null && resourceFilters.isExcluded(resource)); + resource.setExcluded(excluded); + return excluded; + } + + public List getChildren(Resource resource) { + return getChildren(resource, false); + } + + public List getChildren(Resource resource, boolean acceptExcluded) { + List children = Lists.newLinkedList(); + Bucket bucket = getBucket(resource, acceptExcluded); + if (bucket != null) { + for (Bucket childBucket : bucket.getChildren()) { + if (acceptExcluded || !childBucket.isExcluded()) { + children.add(childBucket.getResource()); + } + } + } + return children; + } + + public Resource getParent(Resource resource) { + Bucket bucket = getBucket(resource, false); + if (bucket != null && bucket.getParent() != null) { + return bucket.getParent().getResource(); + } + return null; + } + + public boolean index(Resource resource) { + Bucket bucket = doIndex(resource); + return bucket != null && !bucket.isExcluded(); + } + + private Bucket doIndex(Resource resource) { + if (resource.getParent() != null) { + doIndex(resource.getParent()); + } + return doIndex(resource, resource.getParent()); + } + + public boolean index(Resource resource, Resource parentReference) { + Bucket bucket = doIndex(resource, parentReference); + return bucket != null && !bucket.isExcluded(); + } + + private Bucket doIndex(Resource resource, Resource parentReference) { + Bucket bucket = buckets.get(resource); + if (bucket != null) { + return bucket; + } + + checkLock(resource); + + Resource parent = null; + if (!ResourceUtils.isLibrary(resource)) { + // a library has no parent + parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject); + } + + Bucket parentBucket = getBucket(parent, true); + if (parentBucket == null && parent != null) { + LOG.warn("Resource ignored, parent is not indexed: " + resource); + return null; + } + + resource.setEffectiveKey(createUID(currentProject, resource)); + bucket = new Bucket(resource).setParent(parentBucket); + buckets.put(resource, bucket); + + boolean excluded = checkExclusion(resource, parentBucket); + if (!excluded) { + persistence.saveResource(currentProject, resource, (parentBucket != null ? parentBucket.getResource() : null)); + } + return bucket; + } + + private void checkLock(Resource resource) { + if (lock.isLocked() && !ResourceUtils.isLibrary(resource)) { + if (lock.isFailWhenLocked()) { + throw new SonarException("Index is locked, resource can not be indexed: " + resource); + } + } + } + + private Bucket checkIndexed(Resource resource) { + Bucket bucket = getBucket(resource, true); + if (bucket == null) { + if (lock.isLocked()) { + if (lock.isFailWhenLocked()) { + throw new ResourceNotIndexedException(resource); + } + LOG.warn("Resource will be ignored in next Sonar versions, index is locked: " + resource); + } + if (Scopes.isDirectory(resource) || Scopes.isFile(resource)) { + bucket = doIndex(resource); + } else if (!lock.isLocked()) { + LOG.warn("Resource will be ignored in next Sonar versions, it must be indexed before adding data: " + resource); + } + } + return bucket; + } + + public boolean isExcluded(Resource reference) { + Bucket bucket = getBucket(reference, true); + return bucket != null && bucket.isExcluded(); + } + + public boolean isIndexed(Resource reference, boolean acceptExcluded) { + return getBucket(reference, acceptExcluded) != null; + } + + private Bucket getBucket(Resource resource, boolean acceptExcluded) { + Bucket bucket = null; + if (resource != null) { + bucket = buckets.get(resource); + if (!acceptExcluded && bucket != null && bucket.isExcluded()) { + bucket = null; + } + } + return bucket; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java index 39080fe6a5b..6457cf2cc43 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java @@ -1,188 +1,238 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.batch.index; - -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.apache.commons.lang.StringUtils; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.ResourceFilter; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilters; -import org.sonar.api.measures.MetricFinder; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.*; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.Violation; -import org.sonar.api.utils.SonarException; -import org.sonar.batch.DefaultResourceCreationLock; -import org.sonar.batch.ProjectTree; -import org.sonar.batch.ResourceFilters; -import org.sonar.batch.ViolationFilters; - -public class DefaultIndexTest { - - private DefaultIndex index = null; - private DefaultResourceCreationLock lock; - - @Before - public void createIndex() { - lock = new DefaultResourceCreationLock(); - MetricFinder metricFinder = mock(MetricFinder.class); - when(metricFinder.findByKey("ncloc")).thenReturn(CoreMetrics.NCLOC); - - index = new DefaultIndex(mock(PersistenceManager.class), lock, mock(ProjectTree.class), metricFinder); - Project project = new Project("project"); - - ResourceFilter filter = new ResourceFilter() { - public boolean isIgnored(Resource resource) { - return StringUtils.containsIgnoreCase(resource.getKey(), "excluded"); - } - }; - index.setCurrentProject(project, new ResourceFilters(new ResourceFilter[] { filter }), new ViolationFilters(), RulesProfile.create()); - index.doStart(project); - } - - @Test - public void shouldCreateUID() { - Project project = new Project("my_project"); - assertThat(DefaultIndex.createUID(project, project), is("my_project")); - - JavaPackage javaPackage = new JavaPackage("org.foo"); - assertThat(DefaultIndex.createUID(project, javaPackage), is("my_project:org.foo")); - - Library library = new Library("junit:junit", "4.7"); - assertThat(DefaultIndex.createUID(project, library), is("junit:junit")); - } - - @Test - public void shouldIndexParentOfDeprecatedFiles() { - File file = new File("org/foo/Bar.java"); - assertThat(index.index(file), is(true)); - - Directory reference = new Directory("org/foo"); - assertThat(index.getResource(reference).getName(), is("org/foo")); - assertThat(index.isIndexed(reference, true), is(true)); - assertThat(index.isExcluded(reference), is(false)); - assertThat(index.getChildren(reference).size(), is(1)); - assertThat(index.getParent(reference), is(Project.class)); - } - - @Test - public void shouldIndexTreeOfResources() { - Directory directory = new Directory("org/foo"); - File file = new File("org/foo/Bar.java"); - file.setLanguage(Java.INSTANCE); - - assertThat(index.index(directory), is(true)); - assertThat(index.index(file, directory), is(true)); - - File fileRef = new File("org/foo/Bar.java"); - assertThat(index.getResource(fileRef).getKey(), is("org/foo/Bar.java")); - assertThat(index.getResource(fileRef).getLanguage(), is((Language) Java.INSTANCE)); - assertThat(index.isIndexed(fileRef, true), is(true)); - assertThat(index.isExcluded(fileRef), is(false)); - assertThat(index.getChildren(fileRef).size(), is(0)); - assertThat(index.getParent(fileRef), is(Directory.class)); - } - - @Test - public void shouldIndexLibraryOutsideProjectTree() { - Library lib = new Library("junit", "4.8"); - assertThat(index.index(lib), is(true)); - - Library reference = new Library("junit", "4.8"); - assertThat(index.getResource(reference).getQualifier(), is(Qualifiers.LIBRARY)); - assertThat(index.isIndexed(reference, true), is(true)); - assertThat(index.isExcluded(reference), is(false)); - } - - @Test - public void shouldNotIndexResourceIfParentNotIndexed() { - Directory directory = new Directory("org/other"); - File file = new File("org/foo/Bar.java"); - - assertThat(index.index(file, directory), is(false)); - - File fileRef = new File("org/foo/Bar.java"); - assertThat(index.isIndexed(directory, true), is(false)); - assertThat(index.isIndexed(fileRef, true), is(false)); - assertThat(index.isExcluded(fileRef), is(false)); - assertThat(index.getChildren(fileRef).size(), is(0)); - assertThat(index.getParent(fileRef), nullValue()); - } - - /** - * Only a warning is logged when index is locked. - */ - @Test - public void shouldIndexEvenIfLocked() { - lock.lock(); - - Directory dir = new Directory("org/foo"); - assertThat(index.index(dir), is(true)); - assertThat(index.isIndexed(dir, true), is(true)); - } - - @Test(expected = SonarException.class) - public void shouldFailIfIndexingAndLocked() { - lock.setFailWhenLocked(true); - lock.lock(); - - Directory dir = new Directory("org/foo"); - index.index(dir); - } - - @Test - public void shouldBeExcluded() { - File file = new File("org/foo/ExcludedBar.java"); - assertThat(index.index(file), is(false)); - assertThat(index.isIndexed(file, true), is(true)); - assertThat(index.isIndexed(file, false), is(false)); - assertThat(index.isExcluded(file), is(true)); - } - - @Test - public void shouldIndexResourceWhenAddingMeasure() { - Resource dir = new Directory("org/foo"); - index.addMeasure(dir, new Measure("ncloc").setValue(50.0)); - - assertThat(index.isIndexed(dir, true), is(true)); - assertThat(index.getMeasures(dir, MeasuresFilters.metric("ncloc")).getIntValue(), is(50)); - } - - /** - * See http://jira.codehaus.org/browse/SONAR-2107 - */ - @Test - public void shouldNotFailWhenSavingViolationOnNullRule() { - File file = new File("org/foo/Bar.java"); - Violation violation = Violation.create((Rule) null, file); - index.addViolation(violation); - - assertThat(index.getViolations(file).size(), is(0)); - } -} +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.index; + +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.commons.lang.StringUtils; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.ResourceFilter; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.JavaPackage; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Library; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.Violation; +import org.sonar.api.utils.SonarException; +import org.sonar.api.violations.ViolationQuery; +import org.sonar.batch.DefaultResourceCreationLock; +import org.sonar.batch.ProjectTree; +import org.sonar.batch.ResourceFilters; +import org.sonar.batch.ViolationFilters; + +public class DefaultIndexTest { + + private DefaultIndex index = null; + private DefaultResourceCreationLock lock; + private Rule rule; + + @Before + public void createIndex() { + lock = new DefaultResourceCreationLock(); + MetricFinder metricFinder = mock(MetricFinder.class); + when(metricFinder.findByKey("ncloc")).thenReturn(CoreMetrics.NCLOC); + + index = new DefaultIndex(mock(PersistenceManager.class), lock, mock(ProjectTree.class), metricFinder); + Project project = new Project("project"); + + ResourceFilter filter = new ResourceFilter() { + + public boolean isIgnored(Resource resource) { + return StringUtils.containsIgnoreCase(resource.getKey(), "excluded"); + } + }; + RulesProfile rulesProfile = RulesProfile.create(); + rule = Rule.create("repoKey", "ruleKey", "Rule"); + rulesProfile.activateRule(rule, null); + index.setCurrentProject(project, new ResourceFilters(new ResourceFilter[] { filter }), new ViolationFilters(), rulesProfile); + index.doStart(project); + } + + @Test + public void shouldCreateUID() { + Project project = new Project("my_project"); + assertThat(DefaultIndex.createUID(project, project), is("my_project")); + + JavaPackage javaPackage = new JavaPackage("org.foo"); + assertThat(DefaultIndex.createUID(project, javaPackage), is("my_project:org.foo")); + + Library library = new Library("junit:junit", "4.7"); + assertThat(DefaultIndex.createUID(project, library), is("junit:junit")); + } + + @Test + public void shouldIndexParentOfDeprecatedFiles() { + File file = new File("org/foo/Bar.java"); + assertThat(index.index(file), is(true)); + + Directory reference = new Directory("org/foo"); + assertThat(index.getResource(reference).getName(), is("org/foo")); + assertThat(index.isIndexed(reference, true), is(true)); + assertThat(index.isExcluded(reference), is(false)); + assertThat(index.getChildren(reference).size(), is(1)); + assertThat(index.getParent(reference), is(Project.class)); + } + + @Test + public void shouldIndexTreeOfResources() { + Directory directory = new Directory("org/foo"); + File file = new File("org/foo/Bar.java"); + file.setLanguage(Java.INSTANCE); + + assertThat(index.index(directory), is(true)); + assertThat(index.index(file, directory), is(true)); + + File fileRef = new File("org/foo/Bar.java"); + assertThat(index.getResource(fileRef).getKey(), is("org/foo/Bar.java")); + assertThat(index.getResource(fileRef).getLanguage(), is((Language) Java.INSTANCE)); + assertThat(index.isIndexed(fileRef, true), is(true)); + assertThat(index.isExcluded(fileRef), is(false)); + assertThat(index.getChildren(fileRef).size(), is(0)); + assertThat(index.getParent(fileRef), is(Directory.class)); + } + + @Test + public void shouldIndexLibraryOutsideProjectTree() { + Library lib = new Library("junit", "4.8"); + assertThat(index.index(lib), is(true)); + + Library reference = new Library("junit", "4.8"); + assertThat(index.getResource(reference).getQualifier(), is(Qualifiers.LIBRARY)); + assertThat(index.isIndexed(reference, true), is(true)); + assertThat(index.isExcluded(reference), is(false)); + } + + @Test + public void shouldNotIndexResourceIfParentNotIndexed() { + Directory directory = new Directory("org/other"); + File file = new File("org/foo/Bar.java"); + + assertThat(index.index(file, directory), is(false)); + + File fileRef = new File("org/foo/Bar.java"); + assertThat(index.isIndexed(directory, true), is(false)); + assertThat(index.isIndexed(fileRef, true), is(false)); + assertThat(index.isExcluded(fileRef), is(false)); + assertThat(index.getChildren(fileRef).size(), is(0)); + assertThat(index.getParent(fileRef), nullValue()); + } + + /** + * Only a warning is logged when index is locked. + */ + @Test + public void shouldIndexEvenIfLocked() { + lock.lock(); + + Directory dir = new Directory("org/foo"); + assertThat(index.index(dir), is(true)); + assertThat(index.isIndexed(dir, true), is(true)); + } + + @Test(expected = SonarException.class) + public void shouldFailIfIndexingAndLocked() { + lock.setFailWhenLocked(true); + lock.lock(); + + Directory dir = new Directory("org/foo"); + index.index(dir); + } + + @Test + public void shouldBeExcluded() { + File file = new File("org/foo/ExcludedBar.java"); + assertThat(index.index(file), is(false)); + assertThat(index.isIndexed(file, true), is(true)); + assertThat(index.isIndexed(file, false), is(false)); + assertThat(index.isExcluded(file), is(true)); + } + + @Test + public void shouldIndexResourceWhenAddingMeasure() { + Resource dir = new Directory("org/foo"); + index.addMeasure(dir, new Measure("ncloc").setValue(50.0)); + + assertThat(index.isIndexed(dir, true), is(true)); + assertThat(index.getMeasures(dir, MeasuresFilters.metric("ncloc")).getIntValue(), is(50)); + } + + /** + * See http://jira.codehaus.org/browse/SONAR-2107 + */ + @Test + public void shouldNotFailWhenSavingViolationOnNullRule() { + File file = new File("org/foo/Bar.java"); + Violation violation = Violation.create((Rule) null, file); + index.addViolation(violation); + + assertThat(index.getViolations(file).size(), is(0)); + } + + @Test + public void testGetViolations() { + File file = new File("org/foo/Bar.java"); + Violation violation1 = Violation.create(rule, file); + index.addViolation(violation1); + Violation violation2 = Violation.create(rule, file); + violation2.setSwitchedOff(true); + index.addViolation(violation2); + Violation violation3 = Violation.create(rule, file); + violation3.setSwitchedOff(true); + index.addViolation(violation3); + + assertThat(index.getViolations(file).size(), is(1)); + } + + @Test + public void testGetViolationsWithQuery() { + File file = new File("org/foo/Bar.java"); + Violation violation1 = Violation.create(rule, file); + index.addViolation(violation1); + Violation violation2 = Violation.create(rule, file); + violation2.setSwitchedOff(true); + index.addViolation(violation2); + Violation violation3 = Violation.create(rule, file); + violation3.setSwitchedOff(true); + index.addViolation(violation3); + + assertThat(index.getViolations(ViolationQuery.create().forResource(file).ignoreSwitchedOff(false)).size(), is(3)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetViolationsWithQueryWithNoResource() { + index.getViolations(ViolationQuery.create()); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java index 8ee17e565b8..af59b924da7 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java @@ -1,146 +1,159 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.api.batch; - -import org.sonar.api.design.Dependency; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.Metric; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rules.Violation; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Set; - -/** - * @since 1.10 - */ -public interface DecoratorContext { - - /** - * @return the project in which the decorator is - */ - Project getProject(); - - /** - * @return the resource that is currently decorated - */ - Resource getResource(); - - /** - * Child contexts are read only - */ - List getChildren(); - - // MEASURES - - /** - * Find a measure for the resource - */ - Measure getMeasure(Metric metric); - - /** - * Never return null. - */ - M getMeasures(MeasuresFilter filter); - - /** - * Never return null. - */ - Collection getChildrenMeasures(MeasuresFilter filter); - - /** - * @return the resource children measures for the given metric - */ - Collection getChildrenMeasures(Metric metric); - - /** - * Add a measure on the current resource. It can not be executed from children contexts. - * - * @return the same context - */ - DecoratorContext saveMeasure(Measure measure); - - /** - * Add a measure on the current resource. It can not be executed from children contexts. - * - * @return the current object - */ - DecoratorContext saveMeasure(Metric metric, Double value); - - // DEPENDENCIES - - Dependency saveDependency(Dependency dependency); - - Set getDependencies(); - - Collection getIncomingDependencies(); - - Collection getOutgoingDependencies(); - - // RULES - - /** - * Read-only rule violations. - */ - List getViolations(); - - /** - * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION. - * - * @since 2.5 - * @param force allows to force creation of violation even if it was suppressed by {@link org.sonar.api.rules.ViolationFilter} - */ - DecoratorContext saveViolation(Violation violation, boolean force); - - /** - * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION. - */ - DecoratorContext saveViolation(Violation violation); - - // EVENTS - - /** - * @return the list of events associated to the current resource - */ - List getEvents(); - - /** - * Creates an event for a given date - * - * @param name the event name - * @param description the event description - * @param category the event category - * @param date the event date - * @return the created event - */ - Event createEvent(String name, String description, String category, Date date); - - /** - * Deletes an event - * - * @param event the event to delete - */ - void deleteEvent(Event event); - -} +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.batch; + +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Violation; +import org.sonar.api.violations.ViolationQuery; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * @since 1.10 + */ +public interface DecoratorContext { + + /** + * @return the project in which the decorator is + */ + Project getProject(); + + /** + * @return the resource that is currently decorated + */ + Resource getResource(); + + /** + * Child contexts are read only + */ + List getChildren(); + + // MEASURES + + /** + * Find a measure for the resource + */ + Measure getMeasure(Metric metric); + + /** + * Never return null. + */ + M getMeasures(MeasuresFilter filter); + + /** + * Never return null. + */ + Collection getChildrenMeasures(MeasuresFilter filter); + + /** + * @return the resource children measures for the given metric + */ + Collection getChildrenMeasures(Metric metric); + + /** + * Add a measure on the current resource. It can not be executed from children contexts. + * + * @return the same context + */ + DecoratorContext saveMeasure(Measure measure); + + /** + * Add a measure on the current resource. It can not be executed from children contexts. + * + * @return the current object + */ + DecoratorContext saveMeasure(Metric metric, Double value); + + // DEPENDENCIES + + Dependency saveDependency(Dependency dependency); + + Set getDependencies(); + + Collection getIncomingDependencies(); + + Collection getOutgoingDependencies(); + + // RULES + + /** + * Returns the violations that match the {@link ViolationQuery} parameters. + * + * @since 2.8 + * @param violationQuery + * the request parameters specified as a {@link ViolationQuery} + * @return the list of violations that match those parameters + */ + public abstract List getViolations(ViolationQuery violationQuery); + + /** + * Returns all the active (= non switched-off) violations found on the current resource. + * + * @return the list of violations + */ + List getViolations(); + + /** + * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION. + * + * @since 2.5 + * @param force allows to force creation of violation even if it was suppressed by {@link org.sonar.api.rules.ViolationFilter} + */ + DecoratorContext saveViolation(Violation violation, boolean force); + + /** + * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION. + */ + DecoratorContext saveViolation(Violation violation); + + // EVENTS + + /** + * @return the list of events associated to the current resource + */ + List getEvents(); + + /** + * Creates an event for a given date + * + * @param name the event name + * @param description the event description + * @param category the event category + * @param date the event date + * @return the created event + */ + Event createEvent(String name, String description, String category, Date date); + + /** + * Deletes an event + * + * @param event the event to delete + */ + void deleteEvent(Event event); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java index 02bb82d1153..f2a09f9a6ce 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java @@ -1,162 +1,182 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.api.batch; - -import org.sonar.api.design.Dependency; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.Metric; -import org.sonar.api.resources.DuplicatedSourceException; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectLink; -import org.sonar.api.resources.Resource; -import org.sonar.api.rules.Violation; -import org.sonar.graph.DirectedGraphAccessor; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Set; - -public abstract class SonarIndex implements DirectedGraphAccessor { - - /** - * Indexes a resource as a direct child of project. This method does nothing and returns true if the resource already indexed. - * If the method resource.getParent() does not return null, then this parent will be indexed too. - * - * @return false if the resource is excluded - * @since 2.6 - */ - public abstract boolean index(Resource resource); - - - /** - * Indexes a resource. This method does nothing if the resource is already indexed. - * - * @param resource the resource to index. Not nullable - * @param parentReference a reference to the indexed parent. If null, the resource is indexed as a direct child of project. - * @return false if the parent is not indexed or if the resource is excluded - * @since 2.6 - */ - public abstract boolean index(Resource resource, Resource parentReference); - - /** - * Returns true if the referenced resource is excluded. An excluded resource is not indexed. - * @since 2.6 - */ - public abstract boolean isExcluded(Resource reference); - - /** - * @since 2.6 - */ - public abstract boolean isIndexed(Resource reference, boolean acceptExcluded); - - /** - * Search for an indexed resource. - * - * @param reference the resource reference - * @return the indexed resource, null if it's not indexed - * @since 1.10. Generic types since 2.6. - */ - public abstract R getResource(R reference); - - /** - * @since 2.6 - */ - public abstract Resource getParent(Resource reference); - - /** - * @since 2.6 - */ - - public abstract Collection getChildren(Resource reference); - - /** - * Save the source code of a file. The file must be have been indexed before. - * Note: the source stream is not closed. - * - * @return false if the resource is excluded or not indexed - * @throws org.sonar.api.resources.DuplicatedSourceException - * if the source has already been set on this resource - */ - public abstract void setSource(Resource reference, String source) throws DuplicatedSourceException; - - public abstract Project getProject(); - - public final Collection getResources() { - return getVertices(); - } - - /** - * Indexes the resource. - * @return the indexed resource, even if it's excluded - * @deprecated since 2.6. Use methods index() - */ - @Deprecated - public abstract Resource addResource(Resource resource); - - public abstract Measure getMeasure(Resource resource, Metric metric); - - public abstract M getMeasures(Resource resource, MeasuresFilter filter); - - /** - * @since 2.7 - */ - public abstract List getViolations(Resource resource); - - /** - * @since 2.5 - */ - public abstract void addViolation(Violation violation, boolean force); - - public final void addViolation(Violation violation) { - addViolation(violation, false); - } - - /** - * Warning: the resource is automatically indexed for backward-compatibility, but it should be explictly - * indexed before. Next versions will deactivate this automatic indexation. - * - * @throws SonarException if the metric is unknown. - */ - public abstract Measure addMeasure(Resource resource, Measure measure); - - public abstract Dependency addDependency(Dependency dependency); - - public abstract Set getDependencies(); - - public abstract void addLink(ProjectLink link); - - public abstract void deleteLink(String key); - - public abstract List getEvents(Resource resource); - - public abstract void deleteEvent(Event event); - - public abstract Event addEvent(Resource resource, String name, String description, String category, Date date); - - public final Collection getOutgoingDependencies(Resource from) { - return getOutgoingEdges(from); - } - - public final Collection getIncomingDependencies(Resource to) { - return getIncomingEdges(to); - } -} +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.batch; + +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.DuplicatedSourceException; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectLink; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Violation; +import org.sonar.api.violations.ViolationQuery; +import org.sonar.graph.DirectedGraphAccessor; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +public abstract class SonarIndex implements DirectedGraphAccessor { + + /** + * Indexes a resource as a direct child of project. This method does nothing and returns true if the resource already indexed. + * If the method resource.getParent() does not return null, then this parent will be indexed too. + * + * @return false if the resource is excluded + * @since 2.6 + */ + public abstract boolean index(Resource resource); + + + /** + * Indexes a resource. This method does nothing if the resource is already indexed. + * + * @param resource the resource to index. Not nullable + * @param parentReference a reference to the indexed parent. If null, the resource is indexed as a direct child of project. + * @return false if the parent is not indexed or if the resource is excluded + * @since 2.6 + */ + public abstract boolean index(Resource resource, Resource parentReference); + + /** + * Returns true if the referenced resource is excluded. An excluded resource is not indexed. + * @since 2.6 + */ + public abstract boolean isExcluded(Resource reference); + + /** + * @since 2.6 + */ + public abstract boolean isIndexed(Resource reference, boolean acceptExcluded); + + /** + * Search for an indexed resource. + * + * @param reference the resource reference + * @return the indexed resource, null if it's not indexed + * @since 1.10. Generic types since 2.6. + */ + public abstract R getResource(R reference); + + /** + * @since 2.6 + */ + public abstract Resource getParent(Resource reference); + + /** + * @since 2.6 + */ + + public abstract Collection getChildren(Resource reference); + + /** + * Save the source code of a file. The file must be have been indexed before. + * Note: the source stream is not closed. + * + * @return false if the resource is excluded or not indexed + * @throws org.sonar.api.resources.DuplicatedSourceException + * if the source has already been set on this resource + */ + public abstract void setSource(Resource reference, String source) throws DuplicatedSourceException; + + public abstract Project getProject(); + + public final Collection getResources() { + return getVertices(); + } + + /** + * Indexes the resource. + * @return the indexed resource, even if it's excluded + * @deprecated since 2.6. Use methods index() + */ + @Deprecated + public abstract Resource addResource(Resource resource); + + public abstract Measure getMeasure(Resource resource, Metric metric); + + public abstract M getMeasures(Resource resource, MeasuresFilter filter); + + /** + * Returns the violations that match the {@link ViolationQuery} parameters. + * + * @since 2.8 + * @param violationQuery + * the request parameters specified as a {@link ViolationQuery} + * @return the list of violations that match those parameters + */ + public abstract List getViolations(ViolationQuery violationQuery); + + /** + * Returns all the active (= non switched-off) violations found on the given resource. Equivalent to + * {@link #getViolations(ViolationQuery)} called with ViolationQuery.create().forResource(resource).ignoreSwitchedOff(true) + * as a parameter. + * + * @since 2.7 + * @param the + * resource on which violations are searched + * @return the list of violations + */ + public final List getViolations(Resource resource) { + return getViolations(ViolationQuery.create().forResource(resource).ignoreSwitchedOff(true)); + } + + /** + * @since 2.5 + */ + public abstract void addViolation(Violation violation, boolean force); + + public final void addViolation(Violation violation) { + addViolation(violation, false); + } + + /** + * Warning: the resource is automatically indexed for backward-compatibility, but it should be explictly + * indexed before. Next versions will deactivate this automatic indexation. + * + * @throws SonarException if the metric is unknown. + */ + public abstract Measure addMeasure(Resource resource, Measure measure); + + public abstract Dependency addDependency(Dependency dependency); + + public abstract Set getDependencies(); + + public abstract void addLink(ProjectLink link); + + public abstract void deleteLink(String key); + + public abstract List getEvents(Resource resource); + + public abstract void deleteEvent(Event event); + + public abstract Event addEvent(Resource resource, String name, String description, String category, Date date); + + public final Collection getOutgoingDependencies(Resource from) { + return getOutgoingEdges(from); + } + + public final Collection getIncomingDependencies(Resource to) { + return getIncomingEdges(to); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/violations/ViolationQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/violations/ViolationQuery.java new file mode 100644 index 00000000000..e9f5ebdbdfa --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/violations/ViolationQuery.java @@ -0,0 +1,89 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.violations; + +import org.sonar.api.resources.Resource; + +/** + * Class that allows to query the Sonar index about violations. + * + * @since 2.8 + */ +public final class ViolationQuery { + + private boolean ignoreSwitchedOff; + private Resource resource; + + /** + * Use the factory method create() + */ + ViolationQuery() { + } + + /** + * Creates a new {@link ViolationQuery} object. + * + * @return the new query + */ + public static ViolationQuery create() { + return new ViolationQuery(); + } + + /** + * Specifies if the query should returned switched-off violations or not. + * + * @param ignore + * if true, the query will return only active violations. + * @return the current violation query + */ + public ViolationQuery ignoreSwitchedOff(boolean ignore) { + this.ignoreSwitchedOff = ignore; + return this; + } + + /** + * Tells if the query should returned switched-off violations or not. + * + * @return + */ + public boolean ignoreSwitchedOff() { + return ignoreSwitchedOff; + } + + /** + * Specifies the resource which violations are search from. + * + * @param resource + * the resource + */ + public ViolationQuery forResource(Resource resource) { + this.resource = resource; + return this; + } + + /** + * Returns the resource which violations are search from. + * + * @return the resource + */ + public Resource getResource() { + return resource; + } +} -- 2.39.5