]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4087 first draft of test api
authorSimon Brandhof <simon.brandhof@gmail.com>
Fri, 25 Jan 2013 13:03:52 +0000 (14:03 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Fri, 25 Jan 2013 13:03:52 +0000 (14:03 +0100)
46 files changed:
pom.xml
sonar-application/pom.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskModule.java
sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java
sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
sonar-core/pom.xml
sonar-core/src/main/java/org/sonar/core/component/ComponentGraph.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ComponentWrapper.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ElementWrapper.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ElementWrappers.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/GraphReader.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/GraphStorage.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilders.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/PerspectiveLoaders.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/PerspectiveNotFoundException.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/TransientGraph.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/package-info.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/GraphDao.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/GraphDto.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/GraphDtoMapper.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/GraphWriter.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/MultipleVerticesException.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/graph/package-info.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
sonar-core/src/main/resources/org/sonar/core/graph/GraphDtoMapper.xml [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
sonar-core/src/test/java/org/sonar/core/component/GraphUtilTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/CoveredTestable.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestCase.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestPlan.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestable.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/TestCase.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/TestPlan.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/package-info.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/webapp/WEB-INF/db/migrate/370_create_graphs.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/db/migrate/README.txt

diff --git a/pom.xml b/pom.xml
index 643aa644654abcb798640fd16078c6d57fd4fb5c..b45669c07c3c6db43be31f19c2e1b89edfeee361 100644 (file)
--- a/pom.xml
+++ b/pom.xml
         <artifactId>asm-all</artifactId>
         <version>3.2</version>
       </dependency>
+      <dependency>
+        <groupId>com.tinkerpop.blueprints</groupId>
+        <artifactId>blueprints-core</artifactId>
+        <version>2.2.0</version>
+      </dependency>
+      <dependency>
+        <groupId>com.tinkerpop.gremlin</groupId>
+        <artifactId>gremlin-java</artifactId>
+        <version>2.2.0</version>
+      </dependency>
       <dependency>
         <groupId>org.codehaus.plexus</groupId>
         <artifactId>plexus-classworlds</artifactId>
index 02576867e15f433b24dbbc43fbd3f3634eff9cd2..4ae705539dbf408fd30cb80312e8057d8e40d3e2 100644 (file)
             <configuration>
               <rules>
                 <requireFilesSize>
-                  <maxsize>54000000</maxsize>
+                  <!-- TODO decrease after exclusion of Blueprints -> jackson/jettison -->
+                  <maxsize>55000000</maxsize>
                   <minsize>52000000</minsize>
                   <files>
                     <file>${project.build.directory}/sonar-${project.version}.zip</file>
index d9233cb7f114041241b8f6eaaa0b346225f22fcc..76f1df54be87be8227952e0609dc000742e45e88 100644 (file)
@@ -48,6 +48,9 @@ import org.sonar.batch.index.MemoryOptimizer;
 import org.sonar.batch.index.SourcePersister;
 import org.sonar.batch.tasks.InspectionTask;
 import org.sonar.batch.tasks.ListTasksTask;
+import org.sonar.core.component.ComponentGraph;
+import org.sonar.core.component.GraphStorage;
+import org.sonar.core.component.PerspectiveBuilders;
 import org.sonar.core.i18n.I18nManager;
 import org.sonar.core.i18n.RuleI18nManager;
 import org.sonar.core.metric.CacheMetricFinder;
@@ -58,6 +61,8 @@ import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.persistence.SemaphoresImpl;
 import org.sonar.core.resource.DefaultResourcePermissions;
 import org.sonar.core.rule.CacheRuleFinder;
+import org.sonar.core.test.TestPlanBuilder;
+import org.sonar.core.test.TestableBuilder;
 import org.sonar.core.user.DefaultUserFinder;
 import org.sonar.jpa.dao.MeasuresDao;
 import org.sonar.jpa.session.DefaultDatabaseConnector;
@@ -158,6 +163,13 @@ public class TaskModule extends Module {
     container.addSingleton(DefaultFileLinesContextFactory.class);
     container.addSingleton(ProjectLock.class);
     container.addSingleton(DryRunDatabase.class);
+
+    // graphs
+    container.addSingleton(ComponentGraph.class);
+    container.addSingleton(TestPlanBuilder.class);
+    container.addSingleton(TestableBuilder.class);
+    container.addSingleton(PerspectiveBuilders.class);
+    container.addSingleton(GraphStorage.class);
   }
 
   private void logSettings() {
@@ -172,8 +184,7 @@ public class TaskModule extends Module {
     Task task = container.getComponentByType(taskDefinition.getTask());
     if (task != null) {
       task.execute();
-    }
-    else {
+    } else {
       throw new SonarException("Extension " + taskDefinition.getTask() + " was not found in declared extensions.");
     }
   }
index fee88556e8be243f7134c708832967a23ba2ccef..2ad5f8a493de41d04f2e02d1cbecd5b24b05c7a9 100644 (file)
@@ -29,6 +29,7 @@ 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.database.model.Snapshot;
 import org.sonar.api.design.Dependency;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.MeasuresFilter;
@@ -38,6 +39,7 @@ import org.sonar.api.measures.MetricFinder;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.api.resources.Scopes;
@@ -49,6 +51,7 @@ import org.sonar.batch.DefaultResourceCreationLock;
 import org.sonar.batch.ProjectTree;
 import org.sonar.batch.ResourceFilters;
 import org.sonar.batch.ViolationFilters;
+import org.sonar.core.component.ComponentGraph;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -67,6 +70,7 @@ public class DefaultIndex extends SonarIndex {
   private PersistenceManager persistence;
   private DefaultResourceCreationLock lock;
   private MetricFinder metricFinder;
+  private ComponentGraph graph;
 
   // filters
   private ViolationFilters violationFilters;
@@ -80,11 +84,12 @@ public class DefaultIndex extends SonarIndex {
   private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newHashMap();
   private ProjectTree projectTree;
 
-  public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder) {
+  public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder, ComponentGraph graph) {
     this.persistence = persistence;
     this.lock = lock;
     this.projectTree = projectTree;
     this.metricFinder = metricFinder;
+    this.graph = graph;
   }
 
   public void start() {
@@ -465,10 +470,10 @@ public class DefaultIndex extends SonarIndex {
     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();
+        .append(project.getKey())
+        .append(':')
+        .append(resource.getKey())
+        .toString();
     }
     return uid;
   }
@@ -551,8 +556,12 @@ public class DefaultIndex extends SonarIndex {
 
     boolean excluded = checkExclusion(resource, parentBucket);
     if (!excluded) {
-      persistence.saveResource(currentProject, resource, (parentBucket != null ? parentBucket.getResource() : null));
+      Snapshot snapshot = persistence.saveResource(currentProject, resource, (parentBucket != null ? parentBucket.getResource() : null));
+      if (ResourceUtils.isPersistable(resource) && !Qualifiers.LIBRARY.equals(resource.getQualifier())) {
+        graph.createComponent(resource, snapshot);
+      }
     }
+
     return bucket;
   }
 
index de4ace57ffc029f54fcb826e560e71a2807f2380..080542eb5feb599cc9e723e31f9540a96872a934 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.resources.Project;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.PersistenceManager;
+import org.sonar.core.component.GraphStorage;
 
 import java.util.Collection;
 
@@ -48,12 +49,14 @@ public final class Phases {
   private SensorContext sensorContext;
   private DefaultIndex index;
   private ProjectInitializer pi;
+  private GraphStorage graphStorage;
 
   public Phases(DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
                 MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
                 PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
                 PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
-                EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi) {
+                EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi,
+                GraphStorage graphStorage) {
     this.decoratorsExecutor = decoratorsExecutor;
     this.mavenPhaseExecutor = mavenPhaseExecutor;
     this.mavenPluginsConfigurator = mavenPluginsConfigurator;
@@ -66,15 +69,16 @@ public final class Phases {
     this.eventBus = eventBus;
     this.updateStatusJob = updateStatusJob;
     this.pi = pi;
+    this.graphStorage = graphStorage;
   }
 
   public Phases(DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
                 MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
                 PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
                 PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
-                EventBus eventBus, ProjectInitializer pi) {
+                EventBus eventBus, ProjectInitializer pi, GraphStorage graphStorage) {
     this(decoratorsExecutor, mavenPhaseExecutor, mavenPluginsConfigurator, initializersExecutor, postJobsExecutor,
-      sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi);
+      sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, graphStorage);
   }
 
   /**
@@ -94,6 +98,7 @@ public final class Phases {
     persistenceManager.setDelayedMode(false);
 
     if (project.isRoot()) {
+      graphStorage.save();
       if (updateStatusJob != null) {
         updateStatusJob.execute();
       }
index 19babd292bd3ae92d674b50001ec2d9b179d54f7..75b06ac0fb9fdd7672ceb9abb8229ec9040edb94 100644 (file)
@@ -52,6 +52,7 @@ import org.sonar.batch.DefaultResourceCreationLock;
 import org.sonar.batch.ProjectTree;
 import org.sonar.batch.ResourceFilters;
 import org.sonar.batch.ViolationFilters;
+import org.sonar.core.component.ComponentGraph;
 
 public class DefaultIndexTest {
 
@@ -65,7 +66,7 @@ public class DefaultIndexTest {
     MetricFinder metricFinder = mock(MetricFinder.class);
     when(metricFinder.findByKey("ncloc")).thenReturn(CoreMetrics.NCLOC);
 
-    index = new DefaultIndex(mock(PersistenceManager.class), lock, mock(ProjectTree.class), metricFinder);
+    index = new DefaultIndex(mock(PersistenceManager.class), lock, mock(ProjectTree.class), metricFinder, mock(ComponentGraph.class));
     Project project = new Project("project");
 
     ResourceFilter filter = new ResourceFilter() {
index bc73cee5c9f00bf4254d24921f104abac5359b33..14df0bb57fdf11f90867605b9d5e8cd334bd4f85 100644 (file)
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
       <groupId>com.googlecode.json-simple</groupId>
       <artifactId>json-simple</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.tinkerpop.blueprints</groupId>
+      <artifactId>blueprints-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.tinkerpop.gremlin</groupId>
+      <artifactId>gremlin-java</artifactId>
+    </dependency>
 
     <!-- logging -->
     <dependency>
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentGraph.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentGraph.java
new file mode 100644 (file)
index 0000000..ff28e3a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Element;
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.KeyIndexableGraph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+import com.tinkerpop.blueprints.util.ElementHelper;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.component.Component;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Resource;
+import org.sonar.core.graph.GraphUtil;
+
+public class ComponentGraph implements BatchComponent, ServerComponent {
+  private final KeyIndexableGraph graph;
+  private final ElementWrappers wrapperCache;
+  private final Vertex rootVertex;
+
+  public ComponentGraph() {
+    graph = new TinkerGraph();
+    graph.createKeyIndex("key", Vertex.class);
+    wrapperCache = new ElementWrappers();
+    rootVertex = graph.addVertex(null);
+    rootVertex.setProperty("root", "components");
+  }
+
+  public ComponentGraph(KeyIndexableGraph graph, Vertex rootVertex) {
+    this.graph = graph;
+    this.rootVertex = rootVertex;
+    wrapperCache = new ElementWrappers();
+  }
+
+  public <T extends ElementWrapper> T wrap(Element element, Class<T> wrapperClass) {
+    return wrapperCache.wrap(element, wrapperClass, this);
+  }
+
+  public <T extends ElementWrapper> T wrap(Component component, Class<T> wrapperClass) {
+    Vertex vertex = GraphUtil.single(graph.getVertices("key", component.getKey()));
+    T wrapper = wrapperCache.wrap(vertex, wrapperClass, this);
+    return wrapper;
+  }
+
+  public <T extends ElementWrapper<Vertex>> T createVertex(ElementWrapper<Vertex> from, Class<T> classWrapper, String edgeLabel, String... edgeProperties) {
+    T to = createVertex(classWrapper);
+    Edge edge = graph.addEdge(null, from.element(), to.element(), edgeLabel);
+    ElementHelper.setProperties(edge, edgeProperties);
+    return to;
+  }
+
+  private <T extends ElementWrapper<Vertex>> T createVertex(Class<T> classWrapper) {
+    Vertex vertex = graph.addVertex(null);
+    return wrapperCache.wrap(vertex, classWrapper, this);
+  }
+
+  public <C extends Component<C>> ComponentWrapper<C> createComponent(C component) {
+    Vertex componentVertex = graph.addVertex(null);
+    graph.addEdge(null, rootVertex, componentVertex, "component");
+    ComponentWrapper wrapper = wrapperCache.wrap(componentVertex, ComponentWrapper.class, this);
+    wrapper.populate(component);
+    return wrapper;
+  }
+
+  public ComponentWrapper createComponent(Resource resource, Snapshot snapshot) {
+    return createComponent(new ResourceComponent(resource, snapshot));
+  }
+
+  public Graph getUnderlyingGraph() {
+    return graph;
+  }
+
+  public Vertex getRootVertex() {
+    return rootVertex;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentWrapper.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentWrapper.java
new file mode 100644 (file)
index 0000000..cdd7599
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Vertex;
+import org.sonar.api.component.Component;
+import org.sonar.core.graph.GraphUtil;
+
+import javax.annotation.Nullable;
+
+public class ComponentWrapper<C extends Component<C>> extends ElementWrapper<Vertex> implements Component<C> {
+
+  public String getKey() {
+    return (String) element().getProperty("key");
+  }
+
+  public String getName() {
+    return (String) element().getProperty("name");
+  }
+
+  public String getQualifier() {
+    return (String) element().getProperty("qualifier");
+  }
+
+  public ComponentWrapper setKey(String s) {
+    element().setProperty("key", s);
+    return this;
+  }
+
+  public ComponentWrapper setName(@Nullable String s) {
+    GraphUtil.setNullableProperty(element(), "name", s);
+    return this;
+  }
+
+  public ComponentWrapper setQualifier(String s) {
+    element().setProperty("qualifier", s);
+    return this;
+  }
+
+  public void populate(C component) {
+    setKey(component.getKey());
+    setName(component.getName());
+    setQualifier(component.getQualifier());
+    if (component instanceof ResourceComponent) {
+      element().setProperty("sid", ((ResourceComponent) component).getSnapshotId());
+    }
+  }
+}
\ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ElementWrapper.java b/sonar-core/src/main/java/org/sonar/core/component/ElementWrapper.java
new file mode 100644 (file)
index 0000000..a53b322
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Element;
+
+/**
+ * Wrap a Blueprints vertex or edge.
+ */
+public abstract class ElementWrapper<T extends Element> {
+
+  private T element;
+  private ComponentGraph graph;
+
+  public T element() {
+    return element;
+  }
+
+  void setElement(T element) {
+    this.element = element;
+  }
+
+  public ComponentGraph graph() {
+    return graph;
+  }
+
+  void setGraph(ComponentGraph graph) {
+    this.graph = graph;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ElementWrappers.java b/sonar-core/src/main/java/org/sonar/core/component/ElementWrappers.java
new file mode 100644 (file)
index 0000000..17496c5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.google.common.collect.MapMaker;
+import com.tinkerpop.blueprints.Element;
+
+import java.util.Map;
+
+public class ElementWrappers {
+
+
+  private final Map<ElementKey, ElementWrapper> cache;
+
+  public ElementWrappers() {
+    cache = new MapMaker().weakValues().makeMap();
+  }
+
+  public void clear() {
+    cache.clear();
+  }
+
+  public <T extends ElementWrapper> T wrap(Element element, Class<T> wrapperClass, ComponentGraph graph) {
+    ElementKey key = new ElementKey(element, wrapperClass);
+    T wrapper = (T) cache.get(key);
+    if (wrapper == null) {
+      try {
+      wrapper = (T)key.wrapperClass.newInstance();
+      wrapper.setElement(key.element);
+      wrapper.setGraph(graph);
+      cache.put(key, wrapper);
+      } catch (InstantiationException e) {
+        throw new IllegalStateException("Class has no default constructor: " + wrapperClass, e);
+      } catch (IllegalAccessException e) {
+        throw new IllegalStateException("Can not access to default constructor: " + wrapperClass, e);
+      }
+    }
+    return wrapper;
+  }
+
+  public ElementWrappers remove(Element elt, Class wrapperClass) {
+    cache.remove(new ElementKey(elt, wrapperClass));
+    return this;
+  }
+
+  private static class ElementKey {
+    Element element;
+    Class<? extends ElementWrapper> wrapperClass;
+
+    ElementKey(Element element, Class<? extends ElementWrapper> wrapperClass) {
+      this.element = element;
+      this.wrapperClass = wrapperClass;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+
+      ElementKey that = (ElementKey) o;
+      if (!element.equals(that.element)) {
+        return false;
+      }
+      return wrapperClass.equals(that.wrapperClass);
+    }
+
+    @Override
+    public int hashCode() {
+      int result = element.hashCode();
+      result = 31 * result + wrapperClass.hashCode();
+      return result;
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/GraphReader.java b/sonar-core/src/main/java/org/sonar/core/component/GraphReader.java
new file mode 100644 (file)
index 0000000..c73ba75
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+import com.tinkerpop.blueprints.util.io.graphson.GraphSONReader;
+import org.sonar.core.component.ComponentGraph;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+public class GraphReader {
+
+  public ComponentGraph read(String data, String rootVertexId) {
+    ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes());
+    try {
+      TinkerGraph graph = new TinkerGraph();
+      GraphSONReader.inputGraph(graph, input);
+      Vertex root = graph.getVertex(rootVertexId);
+      return new ComponentGraph(graph, root);
+    } catch (IOException e) {
+      throw new IllegalStateException();
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/GraphStorage.java b/sonar-core/src/main/java/org/sonar/core/component/GraphStorage.java
new file mode 100644 (file)
index 0000000..8789066
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+import org.slf4j.LoggerFactory;
+import org.sonar.core.graph.GraphDto;
+import org.sonar.core.graph.GraphDtoMapper;
+import org.sonar.core.graph.GraphWriter;
+import org.sonar.core.persistence.BatchSession;
+import org.sonar.core.persistence.MyBatis;
+
+public class GraphStorage {
+  private final MyBatis myBatis;
+  private final ComponentGraph componentGraph;
+
+  public GraphStorage(MyBatis myBatis, ComponentGraph componentGraph) {
+    this.myBatis = myBatis;
+    this.componentGraph = componentGraph;
+  }
+
+  public void save() {
+    LoggerFactory.getLogger(GraphStorage.class).info("Persisting graphs of components");
+    BatchSession session = myBatis.openBatchSession();
+    GraphDtoMapper mapper = session.getMapper(GraphDtoMapper.class);
+    try {
+      TinkerGraph subGraph = new TinkerGraph();
+      GraphWriter writer = new GraphWriter();
+      for (Vertex component : componentGraph.getRootVertex().getVertices(Direction.OUT, "component")) {
+        Long snapshotId = (Long) component.getProperty("sid");
+        if (snapshotId != null) {
+          String data = writer.write(componentGraph.getUnderlyingGraph());
+          mapper.insert(new GraphDto()
+            .setData(data).setFormat("graphson").setPerspective("testplan").setVersion(1)
+            .setSnapshotId(snapshotId).setRootVertexId(component.getId().toString()));
+          subGraph.clear();
+        }
+      }
+      session.commit();
+    } finally {
+      session.close();
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java
new file mode 100644 (file)
index 0000000..9155e05
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.component.Perspective;
+
+public abstract class PerspectiveBuilder<T extends Perspective> implements BatchComponent, ServerComponent {
+
+  private final Class<T> perspectiveClass;
+
+  protected PerspectiveBuilder(Class<T> perspectiveClass) {
+    this.perspectiveClass = perspectiveClass;
+  }
+
+  protected Class<T> getPerspectiveClass() {
+    return perspectiveClass;
+  }
+
+  public abstract T build(ComponentWrapper<?> componentWrapper);
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilders.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilders.java
new file mode 100644 (file)
index 0000000..ea7fa5c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.component.Component;
+import org.sonar.api.component.Perspective;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Resource;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Map;
+
+public class PerspectiveBuilders implements ResourcePerspectives, BatchComponent, ServerComponent {
+  private final ComponentGraph graph;
+  private Map<Class<?>, PerspectiveBuilder<?>> builders = Maps.newHashMap();
+  private final Map<Component, Map<Class<Perspective>, Perspective>> components = new MapMaker().weakValues().makeMap();
+  private final SonarIndex resourceIndex;
+
+  public PerspectiveBuilders(ComponentGraph graph, PerspectiveBuilder[] builders, SonarIndex resourceIndex) {
+    this.graph = graph;
+    this.resourceIndex = resourceIndex;
+    for (PerspectiveBuilder builder : builders) {
+      // TODO check duplications
+      this.builders.put(builder.getPerspectiveClass(), builder);
+    }
+  }
+
+  @CheckForNull
+  public <P extends Perspective> P as(Component component, Class<P> toClass) {
+    if (component.getKey() == null) {
+      return null;
+    }
+    Map<Class<Perspective>, Perspective> perspectives = components.get(component);
+    if (perspectives == null) {
+      perspectives = Maps.newHashMap();
+      components.put(component, perspectives);
+    }
+    P perspective = (P) perspectives.get(toClass);
+    if (perspective == null) {
+      ComponentWrapper componentWrapper = graph.wrap(component, ComponentWrapper.class);
+      perspective = builderFor(toClass).build(componentWrapper);
+      perspectives.put((Class) toClass, perspective);
+    }
+    return perspective;
+  }
+
+  public <P extends Perspective> P as(Resource resource, Class<P> toClass) {
+    Resource indexedResource = resourceIndex.getResource(resource);
+    if (indexedResource != null) {
+      return as(new ResourceComponent(indexedResource), toClass);
+    }
+    return null;
+  }
+
+  <T extends Perspective> PerspectiveBuilder<T> builderFor(Class<T> clazz) {
+    PerspectiveBuilder<T> builder = (PerspectiveBuilder<T>) builders.get(clazz);
+    if (builder == null) {
+      throw new PerspectiveNotFoundException("Perspective class is not registered: " + clazz);
+    }
+    return builder;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveLoaders.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveLoaders.java
new file mode 100644 (file)
index 0000000..8aa6e93
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.api.component.Perspective;
+import org.sonar.core.graph.GraphDao;
+import org.sonar.core.graph.GraphDto;
+import org.sonar.core.test.DefaultTestPlan;
+import org.sonar.core.test.DefaultTestable;
+
+import javax.annotation.CheckForNull;
+
+public class PerspectiveLoaders implements ServerComponent {
+
+  private final GraphDao dao;
+
+  public PerspectiveLoaders(GraphDao dao) {
+    this.dao = dao;
+  }
+
+  @CheckForNull
+  <T extends Perspective> T as(String componentKey, String perspectiveKey) {
+    GraphDto graphDto = dao.selectByComponent(perspectiveKey, componentKey);
+    return doAs(perspectiveKey, graphDto);
+  }
+
+  @CheckForNull
+  <T extends Perspective> T as(long snapshotId, String perspectiveKey) {
+    GraphDto graphDto = dao.selectBySnapshot(perspectiveKey, snapshotId);
+    return doAs(perspectiveKey, graphDto);
+  }
+
+  private <T extends Perspective> T doAs(String perspectiveKey, GraphDto graphDto) {
+    T result = null;
+    if (graphDto != null) {
+      ComponentGraph graph = new GraphReader().read(graphDto.getData(), graphDto.getRootVertexId());
+      if (perspectiveKey.equals("testplan")) {
+        result = (T) graph.wrap(graph.getRootVertex(), DefaultTestPlan.class);
+      } else if (perspectiveKey.equals("testable")) {
+        result = (T) graph.wrap(graph.getRootVertex(), DefaultTestable.class);
+      }
+    }
+    return result;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveNotFoundException.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveNotFoundException.java
new file mode 100644 (file)
index 0000000..78c27d5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+public class PerspectiveNotFoundException extends RuntimeException {
+  public PerspectiveNotFoundException(String message) {
+    super(message);
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java b/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java
new file mode 100644 (file)
index 0000000..413de49
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import org.sonar.api.component.Component;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Resource;
+
+import javax.annotation.Nullable;
+
+class ResourceComponent implements Component {
+  private String key;
+  private String name;
+  private String qualifier;
+  private Long snapshotId;
+
+  ResourceComponent(Resource resource, @Nullable Snapshot snapshot) {
+    this.key = resource.getEffectiveKey();
+    this.name = resource.getName();
+    this.qualifier = resource.getQualifier();
+    if (snapshot!=null && snapshot.getId()!=null) {
+      this.snapshotId = snapshot.getId().longValue();
+    }
+  }
+
+  ResourceComponent(Resource resource) {
+    this(resource, null);
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getQualifier() {
+    return qualifier;
+  }
+
+  public Long getSnapshotId() {
+    return snapshotId;
+  }
+}
\ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/component/TransientGraph.java b/sonar-core/src/main/java/org/sonar/core/component/TransientGraph.java
new file mode 100644 (file)
index 0000000..739f865
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+
+public class TransientGraph {
+  private Graph graph;
+  private Vertex root;
+
+  public TransientGraph() {
+    this.graph = new TinkerGraph();
+  }
+
+  public Vertex getRootVertex() {
+    return root;
+  }
+
+  public Graph getUnderlyingGraph() {
+    return graph;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/package-info.java b/sonar-core/src/main/java/org/sonar/core/component/package-info.java
new file mode 100644 (file)
index 0000000..70ba35e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.core.component;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphDao.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphDao.java
new file mode 100644 (file)
index 0000000..cbf357f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.core.persistence.MyBatis;
+
+public class GraphDao {
+  private final MyBatis mybatis;
+
+  public GraphDao(MyBatis mybatis) {
+    this.mybatis = mybatis;
+  }
+
+  public GraphDto selectBySnapshot(String perspectiveKey, long snapshotId) {
+    SqlSession session = mybatis.openBatchSession();
+    try {
+      GraphDtoMapper mapper = session.getMapper(GraphDtoMapper.class);
+      return mapper.selectBySnapshot(perspectiveKey, snapshotId);
+
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
+  public GraphDto selectByComponent(String perspectiveKey, String componentKey) {
+    SqlSession session = mybatis.openBatchSession();
+    try {
+      GraphDtoMapper mapper = session.getMapper(GraphDtoMapper.class);
+      return mapper.selectByComponent(perspectiveKey, componentKey);
+
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphDto.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphDto.java
new file mode 100644 (file)
index 0000000..a8e1633
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+public class GraphDto {
+  private long snapshotId;
+  private String format;
+  private String perspective;
+  private int version;
+  private String rootVertexId;
+  private String data;
+
+  public long getSnapshotId() {
+    return snapshotId;
+  }
+
+  public GraphDto setSnapshotId(long snapshotId) {
+    this.snapshotId = snapshotId;
+    return this;
+  }
+
+  public String getPerspective() {
+    return perspective;
+  }
+
+  public GraphDto setPerspective(String perspective) {
+    this.perspective = perspective;
+    return this;
+  }
+
+  public String getFormat() {
+    return format;
+  }
+
+  public GraphDto setFormat(String format) {
+    this.format = format;
+    return this;
+  }
+
+  public int getVersion() {
+    return version;
+  }
+
+  public GraphDto setVersion(int version) {
+    this.version = version;
+    return this;
+  }
+
+  public String getRootVertexId() {
+    return rootVertexId;
+  }
+
+  public GraphDto setRootVertexId(String rootVertexId) {
+    this.rootVertexId = rootVertexId;
+    return this;
+  }
+
+  public String getData() {
+    return data;
+  }
+
+  public GraphDto setData(String data) {
+    this.data = data;
+    return this;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphDtoMapper.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphDtoMapper.java
new file mode 100644 (file)
index 0000000..96f28c8
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface GraphDtoMapper {
+  void insert(GraphDto graph);
+
+  GraphDto selectBySnapshot(@Param("perspective") String perspectiveKey, @Param("sid") long snapshotId);
+
+  GraphDto selectByComponent(@Param("perspective") String perspectiveKey, @Param("key") String componentKey);
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java
new file mode 100644 (file)
index 0000000..4b56a69
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+import com.google.common.collect.Lists;
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Element;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+import com.tinkerpop.blueprints.util.ElementHelper;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class GraphUtil {
+
+  private GraphUtil() {
+  }
+
+  /**
+   * Get adjacent vertex. It assumes that there are only 0 or 1 results.
+   *
+   * @throws MultipleVerticesException if there are more than 1 adjacent vertices with the given criteria.
+   */
+  @CheckForNull
+  public static Vertex adjacent(Vertex from, Direction direction, String... labels) {
+    Iterator<Vertex> vertices = from.getVertices(direction, labels).iterator();
+    Vertex result = null;
+    if (vertices.hasNext()) {
+      result = vertices.next();
+      if (vertices.hasNext()) {
+        throw new MultipleVerticesException(String.format("More than one vertex adjacent to: %s, direction: %s, labels: ", from, direction, labels));
+      }
+    }
+    return result;
+  }
+
+  public static <T extends Element> T single(Iterable<T> iterable) {
+    Iterator<T> iterator = iterable.iterator();
+    T result = null;
+    if (iterator.hasNext()) {
+      result = iterator.next();
+      if (iterator.hasNext()) {
+        throw new MultipleVerticesException("More than one elements");
+      }
+    }
+    return result;
+  }
+
+  public static void setNullableProperty(Element elt, String key, @Nullable Object value) {
+    if (value == null) {
+      elt.removeProperty(key);
+    } else {
+      elt.setProperty(key, value);
+    }
+  }
+
+  public static void subGraph(GremlinPipeline path, TinkerGraph toGraph) {
+    List<Edge> edges = Lists.newArrayList();
+    if (path.hasNext()) {
+      for (Object element : (Iterable) path.next()) {
+        if (element instanceof Vertex) {
+          Vertex v = (Vertex) element;
+          Vertex toVertex = toGraph.addVertex(v.getId());
+          ElementHelper.copyProperties(v, toVertex);
+        } else if (element instanceof Edge) {
+          edges.add((Edge) element);
+        }
+      }
+      for (Edge edge : edges) {
+        Vertex from = edge.getVertex(Direction.IN);
+        Vertex to = edge.getVertex(Direction.OUT);
+        Edge copyEdge = toGraph.addEdge(edge.getId(), toGraph.getVertex(from.getId()), toGraph.getVertex(to.getId()), edge.getLabel());
+        ElementHelper.copyProperties(edge, copyEdge);
+      }
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphWriter.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphWriter.java
new file mode 100644 (file)
index 0000000..1c64fa5
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.util.io.graphson.GraphSONMode;
+import com.tinkerpop.blueprints.util.io.graphson.GraphSONWriter;
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class GraphWriter {
+
+  public String write(Graph graph) {
+    ByteArrayOutputStream output = new ByteArrayOutputStream();
+    try {
+      GraphSONWriter.outputGraph(graph, output, GraphSONMode.COMPACT);
+      output.flush();
+      output.close();
+      return new String(output.toByteArray());
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to export graph to JSON", e);
+    } finally {
+      IOUtils.closeQuietly(output);
+    }
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/MultipleVerticesException.java b/sonar-core/src/main/java/org/sonar/core/graph/MultipleVerticesException.java
new file mode 100644 (file)
index 0000000..384edd3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.graph;
+
+public class MultipleVerticesException extends RuntimeException {
+  public MultipleVerticesException(String message) {
+    super(message);
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/graph/package-info.java b/sonar-core/src/main/java/org/sonar/core/graph/package-info.java
new file mode 100644 (file)
index 0000000..6471810
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.core.graph;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
index ee008ae64671da465ad8e8c22ff863020b7e77c7..9d550066d2b9b80394f8e651faf8f24dc4185562 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.core.persistence;
 
 import com.google.common.collect.ImmutableList;
+import org.sonar.core.graph.GraphDao;
 import org.sonar.core.dashboard.ActiveDashboardDao;
 import org.sonar.core.dashboard.DashboardDao;
 import org.sonar.core.duplication.DuplicationDao;
@@ -50,6 +51,7 @@ public final class DaoUtils {
       AuthorDao.class,
       DashboardDao.class,
       DuplicationDao.class,
+      GraphDao.class,
       LoadedTemplateDao.class,
       MeasureFilterDao.class,
       PropertiesDao.class,
index 7819ab82d3feaa0088162c62a1a56775ad75172c..2707689c3409c23c699ae00e64d826e0a4889ddb 100644 (file)
@@ -57,6 +57,7 @@ public final class DatabaseUtils {
     "dependencies",
     "duplications_index",
     "events",
+    "graphs",
     "groups",
     "groups_users",
     "group_roles",
index abaede432925cd616318d58b1fd20572f39b8560..1ec8a9fbe28386946ac9771e656ba12139f85f7d 100644 (file)
@@ -32,7 +32,7 @@ import java.util.List;
  */
 public class DatabaseVersion implements BatchComponent, ServerComponent {
 
-  public static final int LAST_VERSION = 363;
+  public static final int LAST_VERSION = 370;
 
   public static enum Status {
     UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
index 2cecc87304a51d6ac2ed420ab5e97df3ce79507f..2ec68c4fdb19edbf5f5ef21328a05f652814fae9 100644 (file)
@@ -38,6 +38,8 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.database.model.MeasureData;
 import org.sonar.api.database.model.MeasureMapper;
 import org.sonar.api.database.model.MeasureModel;
+import org.sonar.core.graph.GraphDto;
+import org.sonar.core.graph.GraphDtoMapper;
 import org.sonar.core.config.Logback;
 import org.sonar.core.dashboard.ActiveDashboardDto;
 import org.sonar.core.dashboard.ActiveDashboardMapper;
@@ -114,6 +116,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadAlias(conf, "Dashboard", DashboardDto.class);
     loadAlias(conf, "Dependency", DependencyDto.class);
     loadAlias(conf, "DuplicationUnit", DuplicationUnitDto.class);
+    loadAlias(conf, "Graph", GraphDto.class);
     loadAlias(conf, "Group", GroupDto.class);
     loadAlias(conf, "GroupRole", GroupRoleDto.class);
     loadAlias(conf, "LoadedTemplate", LoadedTemplateDto.class);
@@ -137,7 +140,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadAlias(conf, "MeasureData", MeasureData.class);
 
     Class<?>[] mappers = {ActiveDashboardMapper.class, AuthorMapper.class, DashboardMapper.class,
-      DependencyMapper.class, DuplicationMapper.class, LoadedTemplateMapper.class, MeasureFilterMapper.class, PropertiesMapper.class, PurgeMapper.class,
+      DependencyMapper.class, DuplicationMapper.class, GraphDtoMapper.class, LoadedTemplateMapper.class, MeasureFilterMapper.class, PropertiesMapper.class, PurgeMapper.class,
       ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceMapper.class, ResourceSnapshotMapper.class, ReviewCommentMapper.class,
       ReviewMapper.class, RoleMapper.class, RuleMapper.class, SchemaMigrationMapper.class, SemaphoreMapper.class, UserMapper.class, WidgetMapper.class, WidgetPropertyMapper.class,
       MeasureMapper.class};
diff --git a/sonar-core/src/main/resources/org/sonar/core/graph/GraphDtoMapper.xml b/sonar-core/src/main/resources/org/sonar/core/graph/GraphDtoMapper.xml
new file mode 100644 (file)
index 0000000..3da0473
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.graph.GraphDtoMapper">
+
+  <select id="selectBySnapshot" parameterType="map" resultType="Graph">
+    SELECT snapshot_id as snapshotId, format, version, perspective, data
+    FROM graphs
+    WHERE snapshot_id = #{sid} AND perspective = #{perspective}
+  </select>
+
+  <select id="selectByComponent" parameterType="map" resultType="Graph">
+    SELECT g.snapshot_id as snapshotId, g.format, g.version, g.perspective, g.data
+    FROM graphs g, snapshots s
+    WHERE g.perspective = #{perspective} AND g.snapshot_id=s.id AND s.islast=${_true} and s.project_id=(
+    select id from projects where enabled=${_true} and kee=#{key} and person_id is null and copy_resource_id is null
+    )
+  </select>
+
+  <insert id="insert" parameterType="Graph" useGeneratedKeys="false">
+    insert into graphs
+    (snapshot_id, format, version, perspective, root_vertex_id, data, created_at, updated_at)
+    values (
+    #{snapshotId}, #{format}, #{version}, #{perspective}, #{rootVertexId},
+    #{data}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
+  </insert>
+
+  <!-- Oracle -->
+  <insert id="insert" databaseId="oracle" parameterType="Graph" useGeneratedKeys="false" keyProperty="id">
+    <selectKey order="BEFORE" resultType="Long" keyProperty="id">
+      select graphs_seq.NEXTVAL from DUAL
+    </selectKey>
+    insert into graphs
+    (id, snapshot_id, format, version, perspective, root_vertex_id, data, created_at, updated_at)
+    values (
+    #{id}, #{snapshotId}, #{format}, #{version}, #{perspective}, #{rootVertexId},
+    #{data}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
+  </insert>
+</mapper>
+
index 7e100e3f97086888837fd5dde5566e53764b40e9..18f965622b9b16fdd8e9309a16c0ab1be1650a43 100644 (file)
@@ -148,6 +148,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('360');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('361');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('362');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('363');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('370');
 
 INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
 ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
index bb29d4e21d2552ae125f36fcbff5ad8e00f9b64e..413c2f65832645e38d220e19cb84ea206ee80853 100644 (file)
@@ -499,6 +499,18 @@ CREATE TABLE "MEASURE_FILTER_FAVOURITES" (
   "CREATED_AT" TIMESTAMP
 );
 
+CREATE TABLE "GRAPHS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "SNAPSHOT_ID" INTEGER NOT NULL,
+  "FORMAT" VARCHAR(20),
+  "PERSPECTIVE" VARCHAR(30),
+  "VERSION" VARCHAR(20),
+  "ROOT_VERTEX_ID" VARCHAR(30),
+  "DATA" CLOB(2147483647),
+  "CREATED_AT" TIMESTAMP,
+  "UPDATED_AT" TIMESTAMP
+);
+
 -- ----------------------------------------------
 -- DDL Statements for indexes
 -- ----------------------------------------------
@@ -607,4 +619,6 @@ CREATE UNIQUE INDEX "UNIQ_AUTHOR_LOGINS" ON "AUTHORS" ("LOGIN");
 
 CREATE INDEX "MEASURE_FILTERS_NAME" ON "MEASURE_FILTERS" ("NAME");
 
-CREATE INDEX "MEASURE_FILTER_FAVS_USERID" ON "MEASURE_FILTER_FAVOURITES" ("USER_ID");
\ No newline at end of file
+CREATE INDEX "MEASURE_FILTER_FAVS_USERID" ON "MEASURE_FILTER_FAVOURITES" ("USER_ID");
+
+CREATE UNIQUE INDEX "GRAPHS_PERSPECTIVES" ON "GRAPHS" ("SNAPSHOT_ID", "PERSPECTIVE");
\ No newline at end of file
diff --git a/sonar-core/src/test/java/org/sonar/core/component/GraphUtilTest.java b/sonar-core/src/test/java/org/sonar/core/component/GraphUtilTest.java
new file mode 100644 (file)
index 0000000..82a2fbd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.core.component;
+
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import org.junit.Test;
+import org.sonar.core.graph.GraphUtil;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GraphUtilTest {
+  @Test
+  public void subGraph() {
+    TinkerGraph graph = new TinkerGraph();
+    Vertex a = graph.addVertex("1");
+    Vertex b = graph.addVertex("2");
+    Vertex c = graph.addVertex("3");
+    graph.addEdge("4", a, b, "likes");
+    graph.addEdge("5", b, c, "has");
+
+    TinkerGraph subGraph = new TinkerGraph();
+    GremlinPipeline pipeline = new GremlinPipeline(a).outE("likes").inV().path();
+    GraphUtil.subGraph(pipeline, subGraph);
+
+    assertThat(subGraph.getVertices()).hasSize(2);
+    assertThat(subGraph.getEdges()).hasSize(1);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/CoveredTestable.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/CoveredTestable.java
new file mode 100644 (file)
index 0000000..a52d747
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import java.util.Collection;
+
+public interface CoveredTestable {
+
+  TestCase testCase();
+  Testable testable();
+  Collection<Integer> lines();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestCase.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestCase.java
new file mode 100644 (file)
index 0000000..66cef5c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import javax.annotation.Nullable;
+
+public interface MutableTestCase extends TestCase {
+  MutableTestCase setStatus(String s);
+
+  MutableTestCase setDurationInMs(@Nullable Long l);
+
+  MutableTestCase setName(String s);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestPlan.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestPlan.java
new file mode 100644 (file)
index 0000000..15d7e8f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import org.sonar.api.component.MutablePerspective;
+
+public interface MutableTestPlan extends TestPlan<MutableTestCase>, MutablePerspective {
+
+  MutableTestCase addTestCase(String key);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestable.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestable.java
new file mode 100644 (file)
index 0000000..87d153e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import org.sonar.api.component.MutablePerspective;
+
+public interface MutableTestable extends Testable, MutablePerspective {
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/TestCase.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/TestCase.java
new file mode 100644 (file)
index 0000000..948353d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import java.util.Collection;
+
+public interface TestCase {
+  String TYPE_UNIT = "unit";
+  String TYPE_INTEGRATION = "integ";
+
+  String STATUS_PASS = "pass";
+  String STATUS_FAIL = "fail";
+
+  // unit test/integration test/...
+  String type();
+
+  /**
+   * Duration in milliseconds
+   */
+  Long durationInMs();
+
+  // pass/fail/...
+  String status();
+
+  /**
+   * The key is not null and unique among the test plan.
+   */
+  String key();
+
+  String name();
+
+  TestPlan testPlan();
+
+  Collection<CoveredTestable> coveredBlocks();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/TestPlan.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/TestPlan.java
new file mode 100644 (file)
index 0000000..c6c6f18
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import org.sonar.api.component.Perspective;
+
+import java.util.List;
+
+public interface TestPlan<T extends TestCase> extends Perspective {
+  List<T> testCases();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java
new file mode 100644 (file)
index 0000000..d28401c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.test;
+
+import org.sonar.api.component.Perspective;
+
+import java.util.List;
+import java.util.SortedSet;
+
+public interface Testable extends Perspective {
+  List<TestCase> coveringTestCases();
+
+  List<TestCase> testCasesCoveringLine(int line);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/package-info.java
new file mode 100644 (file)
index 0000000..0629ea9
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.api.test;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
index eff0770fcf3a6aafff5f522e59cc6b68d4ad2c19..f87b6a1b68dd3cda2c5726dc309a649b5bd0c70a 100644 (file)
@@ -36,6 +36,9 @@ import org.sonar.api.utils.TimeProfiler;
 import org.sonar.api.utils.UriReader;
 import org.sonar.api.workflow.internal.DefaultWorkflow;
 import org.sonar.core.PicoUtils;
+import org.sonar.core.component.ComponentGraph;
+import org.sonar.core.component.PerspectiveBuilders;
+import org.sonar.core.component.PerspectiveLoaders;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.core.i18n.I18nManager;
@@ -55,6 +58,8 @@ import org.sonar.core.persistence.SemaphoresImpl;
 import org.sonar.core.qualitymodel.DefaultModelFinder;
 import org.sonar.core.resource.DefaultResourcePermissions;
 import org.sonar.core.rule.DefaultRuleFinder;
+import org.sonar.core.test.TestPlanBuilder;
+import org.sonar.core.test.TestableBuilder;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.core.user.DefaultUserFinder;
 import org.sonar.core.workflow.ReviewDatabaseStore;
@@ -256,6 +261,12 @@ public final class Platform {
     servicesContainer.addSingleton(DefaultNotificationManager.class);
     servicesContainer.addSingleton(ReviewsNotificationManager.class);
 
+    servicesContainer.addSingleton(ComponentGraph.class);
+    servicesContainer.addSingleton(TestPlanBuilder.class);
+    servicesContainer.addSingleton(TestableBuilder.class);
+    servicesContainer.addSingleton(PerspectiveBuilders.class);
+    servicesContainer.addSingleton(PerspectiveLoaders.class);
+
     ServerExtensionInstaller extensionRegistrar = servicesContainer.getComponentByType(ServerExtensionInstaller.class);
     extensionRegistrar.registerExtensions(servicesContainer);
 
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/370_create_graphs.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/370_create_graphs.rb
new file mode 100644 (file)
index 0000000..0b37503
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 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
+#
+
+#
+# Sonar 3.5
+#
+class CreateGraphs < ActiveRecord::Migration
+  def self.up
+    create_table 'graphs' do |t|
+      t.column 'snapshot_id', :integer, :null => false
+      t.column 'format', :string, :null => true, :limit => 20
+      t.column 'perspective', :string, :null => true, :limit => 30
+      t.column 'version', :string, :null => true, :limit => 20
+      t.column 'root_vertex_id', :string, :null => true, :limit => 30
+      t.column 'data', :text, :null => true
+      t.timestamps
+    end
+    add_index 'graphs', ['snapshot_id', 'perspective'], :name => 'graphs_perspectives', :unique => true
+  end
+end
+
index dcc2a5250ec8a363ff95ff8a2189e15e97c20844..c9b1b28eb5ae0a1754872a7f8954bf321e6e258f 100644 (file)
@@ -1,7 +1,7 @@
 HOW TO ADD A MIGRATION
 
 * Jump some versions when adding the first Ruby on Rails migration of a new sonar version. For example if sonar 2.10 is 193, then sonar 2.11 should start at 200.
-* Complete the DDL files for Derby :
+* Complete the DDL files for H2 :
   + sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
   + sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql :
     - add "INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('<THE MIGRATION ID>')"