← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 4175: Implemented three of new objects as "ChartGroup", "ReportGroup, "ReportTableGroup". Working in pr...

 

------------------------------------------------------------
revno: 4175
committer: Hieu <hieu.hispvietnam@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2011-07-21 10:29:35 +0700
message:
  Implemented three of new objects as "ChartGroup", "ReportGroup, "ReportTableGroup". Working in process...
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartGroup.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportGroup.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableGroup.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/ChartGroup.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/ReportGroup.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTableGroup.hbm.xml
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/Report.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/report/impl/DefaultReportService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/Report.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml
  dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml
  dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/UUIdUtils.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java	2011-07-07 13:29:56 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java	2011-07-21 03:29:35 +0000
@@ -29,7 +29,9 @@
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.hisp.dhis.common.ImportableObject;
 import org.hisp.dhis.dataelement.DataElement;
@@ -103,6 +105,8 @@
 
     private String targetLineLabel;
 
+    private List<ChartGroup> groups = new ArrayList<ChartGroup>();
+
     private List<Indicator> indicators = new ArrayList<Indicator>();
 
     private List<DataElement> dataElements = new ArrayList<DataElement>();
@@ -155,6 +159,38 @@
     }
 
     // -------------------------------------------------------------------------
+    // Logic
+    // -------------------------------------------------------------------------
+
+    public void addChartGroup( ChartGroup group )
+    {
+        groups.add( group );
+        group.getMembers().add( this );
+    }
+
+    public void removeChartGroup( ChartGroup group )
+    {
+        groups.remove( group );
+        group.getMembers().remove( this );
+    }
+
+    public void updateChartGroups( Set<ChartGroup> updates )
+    {
+        for ( ChartGroup group : new HashSet<ChartGroup>( groups ) )
+        {
+            if ( !updates.contains( group ) )
+            {
+                removeChartGroup( group );
+            }
+        }
+        
+        for ( ChartGroup group : updates )
+        {
+            addChartGroup( group );
+        }
+    }
+
+    // -------------------------------------------------------------------------
     // hashCode, equals, toString
     // -------------------------------------------------------------------------
 
@@ -519,4 +555,14 @@
     {
         this.allOrganisationUnits = allOrganisationUnits;
     }
+
+    public List<ChartGroup> getGroups()
+    {
+        return groups;
+    }
+
+    public void setGroups( List<ChartGroup> groups )
+    {
+        this.groups = groups;
+    }
 }

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartGroup.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartGroup.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartGroup.java	2011-07-21 03:29:35 +0000
@@ -0,0 +1,146 @@
+package org.hisp.dhis.chart;
+
+/*
+ * Copyright (c) 2004-2011, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hisp.dhis.common.AbstractIdentifiableObject;
+
+/**
+ * @author Dang Duy Hieu
+ * @version $Id$
+ */
+public class ChartGroup
+    extends AbstractIdentifiableObject
+{
+    /**
+     * Determines if a de-serialized file is compatible with this class.
+     */
+    private static final long serialVersionUID = -1L;
+
+    private Set<Chart> members = new HashSet<Chart>();
+    
+    // -------------------------------------------------------------------------
+    // Constructors
+    // -------------------------------------------------------------------------
+
+    public ChartGroup()
+    {
+    }
+
+    public ChartGroup( String name )
+    {
+        this.name = name;
+    }
+
+    // -------------------------------------------------------------------------
+    // Logic
+    // -------------------------------------------------------------------------
+
+    public void addChart( Chart chart )
+    {
+        members.add( chart );
+        chart.getGroups().add( this );
+    }
+    
+    public void removeChart( Chart chart )
+    {
+        members.remove( chart );
+        chart.getGroups().remove( this );
+    }
+    
+    public void updateCharts( Set<Chart> updates )
+    {
+        for ( Chart chart : new HashSet<Chart>( members ) )
+        {
+            if ( !updates.contains( chart ) )
+            {
+                removeChart( chart );
+            }
+        }
+        
+        for ( Chart chart : updates )
+        {
+            addChart( chart );
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // hashCode and equals
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int hashCode()
+    {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( !(o instanceof ChartGroup) )
+        {
+            return false;
+        }
+
+        final ChartGroup other = (ChartGroup) o;
+
+        return name.equals( other.getName() );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + name + "]";
+    }
+
+    // -------------------------------------------------------------------------
+    // Getters and setters
+    // -------------------------------------------------------------------------
+
+    public Set<Chart> getMembers()
+    {
+        return members;
+    }
+
+    public void setMembers( Set<Chart> members )
+    {
+        this.members = members;
+    }
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartService.java	2011-06-07 16:12:23 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/ChartService.java	2011-07-21 03:29:35 +0000
@@ -81,4 +81,32 @@
 
     int getChartCountByName( String name );
 
+    // -------------------------------------------------------------------------
+    // ChartGroup
+    // -------------------------------------------------------------------------
+
+    int addChartGroup( ChartGroup chartGroup );
+
+    void updateChartGroup( ChartGroup chartGroup );
+
+    void deleteChartGroup( ChartGroup chartGroup );
+
+    ChartGroup getChartGroup( int id );
+
+    ChartGroup getChartGroupByName( String name );
+
+    Collection<ChartGroup> getAllChartGroups();
+
+    Collection<ChartGroup> getChartGroups( final Collection<Integer> identifiers );
+
+    Collection<ChartGroup> getGroupsContainingChart( Chart chart );
+
+    int getChartGroupCount();
+
+    int getChartGroupCountByName( String name );
+
+    Collection<ChartGroup> getChartGroupsBetween( int first, int max );
+
+    Collection<ChartGroup> getChartGroupsBetweenByName( String name, int first, int max );
+
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/Report.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/Report.java	2011-05-05 21:14:56 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/Report.java	2011-07-21 03:29:35 +0000
@@ -1,7 +1,7 @@
 package org.hisp.dhis.report;
 
 /*
- * Copyright (c) 2004-2010, University of Oslo
+ * Copyright (c) 2004-2011, University of Oslo
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,11 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import org.hisp.dhis.common.AbstractIdentifiableObject;
 import org.hisp.dhis.reporttable.ReportTable;
 
@@ -43,19 +48,21 @@
     private static final long serialVersionUID = 7880117720157807526L;
 
     public static final String TEMPLATE_DIR = "templates";
-    
+
     private String designContent;
-         
+
     private ReportTable reportTable;
-    
+
+    private List<ReportGroup> groups = new ArrayList<ReportGroup>();
+
     // -------------------------------------------------------------------------
     // Constructors
     // -------------------------------------------------------------------------
 
     public Report()
-    {   
+    {
     }
-    
+
     public Report( String name, String designContent, ReportTable reportTable )
     {
         this.name = name;
@@ -67,11 +74,39 @@
     // Logic
     // -------------------------------------------------------------------------
 
+    public void addReportGroup( ReportGroup group )
+    {
+        groups.add( group );
+        group.getMembers().add( this );
+    }
+
+    public void removeReportGroup( ReportGroup group )
+    {
+        groups.remove( group );
+        group.getMembers().remove( this );
+    }
+
+    public void updateReportGroups( Set<ReportGroup> updates )
+    {
+        for ( ReportGroup group : new HashSet<ReportGroup>( groups ) )
+        {
+            if ( !updates.contains( group ) )
+            {
+                removeReportGroup( group );
+            }
+        }
+
+        for ( ReportGroup group : updates )
+        {
+            addReportGroup( group );
+        }
+    }
+
     public boolean hasReportTable()
     {
         return reportTable != null;
     }
-        
+
     // -------------------------------------------------------------------------
     // Equals and hashCode
     // -------------------------------------------------------------------------
@@ -80,11 +115,11 @@
     public int hashCode()
     {
         final int prime = 31;
-        
+
         int result = 1;
-        
-        result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
-        
+
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+
         return result;
     }
 
@@ -95,21 +130,21 @@
         {
             return true;
         }
-        
+
         if ( object == null )
         {
             return false;
         }
-        
+
         if ( getClass() != object.getClass() )
         {
             return false;
         }
-        
+
         final Report other = (Report) object;
-        
+
         return this.name.equals( other.getName() );
-    }    
+    }
 
     // -------------------------------------------------------------------------
     // Getters and setters
@@ -134,4 +169,14 @@
     {
         this.reportTable = reportTable;
     }
+
+    public List<ReportGroup> getGroups()
+    {
+        return groups;
+    }
+
+    public void setGroups( List<ReportGroup> groups )
+    {
+        this.groups = groups;
+    }
 }

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportGroup.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportGroup.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportGroup.java	2011-07-21 03:29:35 +0000
@@ -0,0 +1,146 @@
+package org.hisp.dhis.report;
+
+/*
+ * Copyright (c) 2004-2011, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hisp.dhis.common.AbstractIdentifiableObject;
+
+/**
+ * @author Dang Duy Hieu
+ * @version $Id$
+ */
+public class ReportGroup
+    extends AbstractIdentifiableObject
+{
+    /**
+     * Determines if a de-serialized file is compatible with this class.
+     */
+    private static final long serialVersionUID = -1L;
+
+    private Set<Report> members = new HashSet<Report>();
+    
+    // -------------------------------------------------------------------------
+    // Constructors
+    // -------------------------------------------------------------------------
+
+    public ReportGroup()
+    {
+    }
+
+    public ReportGroup( String name )
+    {
+        this.name = name;
+    }
+
+    // -------------------------------------------------------------------------
+    // Logic
+    // -------------------------------------------------------------------------
+
+    public void addReport( Report report )
+    {
+        members.add( report );
+        report.getGroups().add( this );
+    }
+    
+    public void removeReport( Report report )
+    {
+        members.remove( report );
+        report.getGroups().remove( this );
+    }
+    
+    public void updateReports( Set<Report> updates )
+    {
+        for ( Report report : new HashSet<Report>( members ) )
+        {
+            if ( !updates.contains( report ) )
+            {
+                removeReport( report );
+            }
+        }
+        
+        for ( Report report : updates )
+        {
+            addReport( report );
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // hashCode and equals
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int hashCode()
+    {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( !(o instanceof ReportGroup) )
+        {
+            return false;
+        }
+
+        final ReportGroup other = (ReportGroup) o;
+
+        return name.equals( other.getName() );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + name + "]";
+    }
+
+    // -------------------------------------------------------------------------
+    // Getters and setters
+    // -------------------------------------------------------------------------
+
+    public Set<Report> getMembers()
+    {
+        return members;
+    }
+
+    public void setMembers( Set<Report> members )
+    {
+        this.members = members;
+    }
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportService.java	2010-07-28 13:44:59 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/report/ReportService.java	2011-07-21 03:29:35 +0000
@@ -29,6 +29,9 @@
 
 import java.util.Collection;
 
+import org.hisp.dhis.report.Report;
+import org.hisp.dhis.report.ReportGroup;
+
 /**
  * @author Lars Helge Overland
  * @version $Id$
@@ -36,7 +39,7 @@
 public interface ReportService
 {
     final String ID = ReportService.class.getName();
-    
+
     /**
      * Saves a Report.
      * 
@@ -44,7 +47,7 @@
      * @return the generated identifier.
      */
     int saveReport( Report report );
-    
+
     /**
      * Retrieves the Report with the given identifier.
      * 
@@ -52,21 +55,21 @@
      * @return the Report.
      */
     Report getReport( int id );
-    
+
     /**
      * Deletes a Report.
      * 
      * @param report the Report to delete.
      */
     void deleteReport( Report report );
-    
+
     /**
      * Retrieves all Reports.
      * 
      * @return a Collection of Reports.
      */
     Collection<Report> getAllReports();
-    
+
     /**
      * Retrieves the Report with the given name.
      * 
@@ -74,7 +77,7 @@
      * @return the Report.
      */
     Report getReportByName( String name );
-    
+
     /**
      * Retrieves all Reports with the given identifiers.
      * 
@@ -82,4 +85,32 @@
      * @return a Collection of Reports.
      */
     Collection<Report> getReports( final Collection<Integer> identifiers );
+
+    // -------------------------------------------------------------------------
+    // ReportGroup
+    // -------------------------------------------------------------------------
+
+    int addReportGroup( ReportGroup reportGroup );
+
+    void updateReportGroup( ReportGroup reportGroup );
+
+    void deleteReportGroup( ReportGroup reportGroup );
+
+    ReportGroup getReportGroup( int id );
+
+    ReportGroup getReportGroupByName( String name );
+
+    Collection<ReportGroup> getAllReportGroups();
+
+    Collection<ReportGroup> getReportGroups( final Collection<Integer> identifiers );
+
+    Collection<ReportGroup> getGroupsContainingReport( Report report );
+
+    int getReportGroupCount();
+
+    int getReportGroupCountByName( String name );
+
+    Collection<ReportGroup> getReportGroupsBetween( int first, int max );
+
+    Collection<ReportGroup> getReportGroupsBetweenByName( String name, int first, int max );
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java	2011-05-22 12:19:06 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java	2011-07-21 03:29:35 +0000
@@ -31,8 +31,10 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.hisp.dhis.common.AbstractNameableObject;
@@ -51,8 +53,9 @@
 import org.hisp.dhis.period.comparator.AscendingPeriodComparator;
 
 /**
- * The ReportTable object represents a customizable database table. It has features
- * like crosstabulation, relative periods, parameters, and display columns.
+ * The ReportTable object represents a customizable database table. It has
+ * features like crosstabulation, relative periods, parameters, and display
+ * columns.
  * 
  * @author Lars Helge Overland
  * @version $Id$
@@ -66,68 +69,97 @@
     private static final long serialVersionUID = 5618655666320890565L;
 
     public static final String DATAELEMENT_ID = "dataelementid";
+
     public static final String DATAELEMENT_NAME = "dataelementname";
+
     public static final String CATEGORYCOMBO_ID = "categoryoptioncomboid";
+
     public static final String CATEGORYCOMBO_NAME = "categoryoptioncomboname";
+
     public static final String CATEGORYOPTION_ID = "categoryoptionid";
+
     public static final String CATEGORYOPTION_NAME = "categoryoptionname";
+
     public static final String INDICATOR_ID = "indicatorid";
+
     public static final String INDICATOR_NAME = "indicatorname";
+
     public static final String DATASET_ID = "datasetid";
+
     public static final String DATASET_NAME = "datasetname";
+
     public static final String PERIOD_ID = "periodid";
+
     public static final String PERIOD_NAME = "periodname";
+
     public static final String ORGANISATIONUNIT_ID = "organisationunitid";
+
     public static final String ORGANISATIONUNIT_NAME = "organisationunitname";
 
     public static final String REPORTING_MONTH_COLUMN_NAME = "reporting_month_name";
+
     public static final String PARAM_ORGANISATIONUNIT_COLUMN_NAME = "param_organisationunit_name";
+
     public static final String ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME = "organisation_unit_is_parent";
-    
+
     public static final String SEPARATOR = "_";
-    public static final String SPACE = " ";    
+
+    public static final String SPACE = " ";
+
     public static final String TOTAL_COLUMN_NAME = "total";
+
     public static final String TOTAL_COLUMN_PRETTY_NAME = "Total";
-    
+
     public static final int ASC = -1;
+
     public static final int DESC = 1;
+
     public static final int NONE = 0;
-    
-    public static final Map<String, String> PRETTY_COLUMNS = new HashMap<String, String>() {
-        private static final long serialVersionUID = 4194194769957136714L; {
-        put( DATAELEMENT_ID, "Data element ID" );
-        put( DATAELEMENT_NAME, "Data element" );
-        put( CATEGORYCOMBO_ID, "Category combination ID" );
-        put( CATEGORYCOMBO_NAME, "Category combination" );
-        put( INDICATOR_ID, "Indicator ID" );
-        put( INDICATOR_NAME, "Indicator" );
-        put( DATASET_ID, "Data set ID" );
-        put( DATASET_NAME, "Data set" );
-        put( PERIOD_ID, "Period ID" );
-        put( PERIOD_NAME, "Period" );        
-        put( ORGANISATIONUNIT_ID, "Organisation unit ID" );
-        put( ORGANISATIONUNIT_NAME, "Organisation unit" );        
-        put( REPORTING_MONTH_COLUMN_NAME, "Reporting month" );
-        put( PARAM_ORGANISATIONUNIT_COLUMN_NAME, "Organisation unit parameter" );        
-        put( ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME, "Organisation unit is parent" );
-    } };
-    
-    public static final Map<Class<? extends NameableObject>, String> CLASS_ID_MAP = new HashMap<Class<? extends NameableObject>, String>() {
-        private static final long serialVersionUID = 4742098364404485991L; {
-        put( Indicator.class, INDICATOR_ID );
-        put( DataElement.class, DATAELEMENT_ID );
-        put( DataElementCategoryOptionCombo.class, CATEGORYCOMBO_ID );
-        put( DataElementCategoryOption.class, CATEGORYOPTION_ID );
-        put( DataSet.class, DATASET_ID );
-        put( Period.class, PERIOD_ID );
-        put( OrganisationUnit.class, ORGANISATIONUNIT_ID );
-    } };
+
+    public static final Map<String, String> PRETTY_COLUMNS = new HashMap<String, String>()
+    {
+        private static final long serialVersionUID = 4194194769957136714L;
+        {
+            put( DATAELEMENT_ID, "Data element ID" );
+            put( DATAELEMENT_NAME, "Data element" );
+            put( CATEGORYCOMBO_ID, "Category combination ID" );
+            put( CATEGORYCOMBO_NAME, "Category combination" );
+            put( INDICATOR_ID, "Indicator ID" );
+            put( INDICATOR_NAME, "Indicator" );
+            put( DATASET_ID, "Data set ID" );
+            put( DATASET_NAME, "Data set" );
+            put( PERIOD_ID, "Period ID" );
+            put( PERIOD_NAME, "Period" );
+            put( ORGANISATIONUNIT_ID, "Organisation unit ID" );
+            put( ORGANISATIONUNIT_NAME, "Organisation unit" );
+            put( REPORTING_MONTH_COLUMN_NAME, "Reporting month" );
+            put( PARAM_ORGANISATIONUNIT_COLUMN_NAME, "Organisation unit parameter" );
+            put( ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME, "Organisation unit is parent" );
+        }
+    };
+
+    public static final Map<Class<? extends NameableObject>, String> CLASS_ID_MAP = new HashMap<Class<? extends NameableObject>, String>()
+    {
+        private static final long serialVersionUID = 4742098364404485991L;
+        {
+            put( Indicator.class, INDICATOR_ID );
+            put( DataElement.class, DATAELEMENT_ID );
+            put( DataElementCategoryOptionCombo.class, CATEGORYCOMBO_ID );
+            put( DataElementCategoryOption.class, CATEGORYOPTION_ID );
+            put( DataSet.class, DATASET_ID );
+            put( Period.class, PERIOD_ID );
+            put( OrganisationUnit.class, ORGANISATIONUNIT_ID );
+        }
+    };
 
     private static final String EMPTY = "";
+
     private static final NameableObject[] IRT = new NameableObject[0];
-    private static final String[] SRT = new String[0];    
+
+    private static final String[] SRT = new String[0];
+
     private static final String ILLEGAL_FILENAME_CHARS_REGEX = "[/\\?%*:|\"'<>.]";
-    
+
     // -------------------------------------------------------------------------
     // Persisted properties
     // -------------------------------------------------------------------------
@@ -141,42 +173,48 @@
      * The list of DataElements the ReportTable contains.
      */
     private List<DataElement> dataElements = new ArrayList<DataElement>();
-    
+
     /**
      * The list of Indicators the ReportTable contains.
      */
     private List<Indicator> indicators = new ArrayList<Indicator>();
-    
+
     /**
      * The list of DataSets the ReportTable contains.
      */
     private List<DataSet> dataSets = new ArrayList<DataSet>();
-    
+
     /**
      * The list of Periods the ReportTable contains.
      */
     private List<Period> periods = new ArrayList<Period>();
-    
+
     /**
      * The list of OrganisationUnits the ReportTable contains.
      */
     private List<OrganisationUnit> units = new ArrayList<OrganisationUnit>();
-    
+
+    /**
+     * The list of OrganisationUnits the ReportTable contains.
+     */
+    private List<ReportTableGroup> groups = new ArrayList<ReportTableGroup>();
+
     /**
      * The DataElementCategoryCombo for the ReportTable.
      */
     private DataElementCategoryCombo categoryCombo;
-    
+
     /**
-     * Whether to crosstabulate on the Indicator dimension, which also represents DataElements and DataSets.
+     * Whether to crosstabulate on the Indicator dimension, which also
+     * represents DataElements and DataSets.
      */
     private boolean doIndicators;
-    
+
     /**
      * Whether to crosstabulate on the Period dimension.
      */
     private boolean doPeriods;
-    
+
     /**
      * Whether to crosstabulate on the OrganisationUnit dimension.
      */
@@ -191,12 +229,12 @@
      * The ReportParams of the ReportTable.
      */
     private ReportParams reportParams;
-    
+
     /**
      * The sort order if any applied to the last column of the table.
      */
     private Integer sortOrder;
-    
+
     /**
      * Inidicates whether the table should be limited from top by this value.
      */
@@ -205,22 +243,22 @@
     // -------------------------------------------------------------------------
     // Transient properties
     // -------------------------------------------------------------------------
-    
+
     /**
      * Periods relative to the reporting month.
      */
     private List<Period> relativePeriods = new ArrayList<Period>();
-    
+
     /**
      * Static Periods and relative Periods.
      */
     private List<Period> allPeriods = new ArrayList<Period>();
-    
+
     /**
      * OrganisationUnits relative to a parent unit or current unit.
      */
     private List<OrganisationUnit> relativeUnits = new ArrayList<OrganisationUnit>();
-    
+
     /**
      * Static OrganisationUnits and relative OrganisationUnits.
      */
@@ -235,35 +273,36 @@
      * All crosstabulated columns.
      */
     private List<List<NameableObject>> columns = new ArrayList<List<NameableObject>>();
-    
+
     /**
      * All rows.
      */
     private List<List<NameableObject>> rows = new ArrayList<List<NameableObject>>();
-    
+
     /**
-     * Names of the columns used to query the datavalue table and as index columns
-     * in the report table.
+     * Names of the columns used to query the datavalue table and as index
+     * columns in the report table.
      */
     private List<String> indexColumns = new ArrayList<String>();
-    
+
     /**
-     * Names of the columns holding entry names used to query the datavalue table.
+     * Names of the columns holding entry names used to query the datavalue
+     * table.
      */
     private List<String> indexNameColumns = new ArrayList<String>();
-    
+
     /**
      * The I18nFormat used for internationalization of ie. periods.
      */
     private I18nFormat i18nFormat;
-    
+
     /**
      * The name of the reporting month based on the report param.
      */
     private String reportingMonthName;
-    
+
     /**
-     * The name of the (parent) organisation unit based on the report param. 
+     * The name of the (parent) organisation unit based on the report param.
      */
     private String organisationUnitName;
 
@@ -271,7 +310,7 @@
      * The category option combos derived from the dimension set.
      */
     private List<DataElementCategoryOptionCombo> categoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>();
-    
+
     // -------------------------------------------------------------------------
     // Constructors
     // -------------------------------------------------------------------------
@@ -280,9 +319,9 @@
      * Constructor for persistence purposes.
      */
     public ReportTable()
-    {   
+    {
     }
-    
+
     /**
      * Default constructor.
      * 
@@ -292,36 +331,29 @@
      * @param dataElements the data elements.
      * @param indicators the indicators.
      * @param dataSets the datasets.
-     * @param periods the periods. These periods cannot have the name property set.
-     * @param relativePeriods the relative periods. These periods must have the name property set. Not persisted.
+     * @param periods the periods. These periods cannot have the name property
+     *        set.
+     * @param relativePeriods the relative periods. These periods must have the
+     *        name property set. Not persisted.
      * @param units the organisation units.
      * @param relativeUnits the organisation units. Not persisted.
      * @param dimensionSet the dimension set. Not persisted.
-     * @param doIndicators indicating whether indicators should be crosstabulated.
-     * @param doCategoryOptionCombos indicating whether category option combos should be crosstabulated.
+     * @param doIndicators indicating whether indicators should be
+     *        crosstabulated.
+     * @param doCategoryOptionCombos indicating whether category option combos
+     *        should be crosstabulated.
      * @param doPeriods indicating whether periods should be crosstabulated.
-     * @param doUnits indicating whether organisation units should be crosstabulated.
+     * @param doUnits indicating whether organisation units should be
+     *        crosstabulated.
      * @param relatives the relative periods.
      * @param i18nFormat the i18n format. Not persisted.
      * @param reportingMonthName the reporting month name. Not persisted.
      */
-    public ReportTable( String name,
-        boolean regression,
-        List<DataElement> dataElements,
-        List<Indicator> indicators,
-        List<DataSet> dataSets,
-        List<Period> periods,
-        List<Period> relativePeriods,
-        List<OrganisationUnit> units,
-        List<OrganisationUnit> relativeUnits,
-        DataElementCategoryCombo categoryCombo,
-        boolean doIndicators,
-        boolean doPeriods,
-        boolean doUnits,
-        RelativePeriods relatives,
-        ReportParams reportParams,
-        I18nFormat i18nFormat,
-        String reportingMonthName )
+    public ReportTable( String name, boolean regression, List<DataElement> dataElements, List<Indicator> indicators,
+        List<DataSet> dataSets, List<Period> periods, List<Period> relativePeriods, List<OrganisationUnit> units,
+        List<OrganisationUnit> relativeUnits, DataElementCategoryCombo categoryCombo, boolean doIndicators,
+        boolean doPeriods, boolean doUnits, RelativePeriods relatives, ReportParams reportParams,
+        I18nFormat i18nFormat, String reportingMonthName )
     {
         this.name = name;
         this.regression = regression;
@@ -348,19 +380,21 @@
 
     public void init()
     {
-        verify( nonEmptyLists( dataElements, indicators, dataSets ) > 0, "Must contain dataelements, indicators or datasets" );
+        verify( nonEmptyLists( dataElements, indicators, dataSets ) > 0,
+            "Must contain dataelements, indicators or datasets" );
         verify( nonEmptyLists( periods, relativePeriods ) > 0, "Must contain periods or relative periods" );
-        verify( nonEmptyLists( units, relativeUnits ) > 0, "Must contain organisation units or relative organisation units" );
-        verify( !( doTotal() && regression ), "Cannot have regression columns with total columns" );
+        verify( nonEmptyLists( units, relativeUnits ) > 0,
+            "Must contain organisation units or relative organisation units" );
+        verify( !(doTotal() && regression), "Cannot have regression columns with total columns" );
         verify( i18nFormat != null, "I18n format must be set" );
-        
+
         // ---------------------------------------------------------------------
         // Init dimensions
         // ---------------------------------------------------------------------
 
         if ( isDimensional() )
         {
-            categoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>( categoryCombo.getOptionCombos() );            
+            categoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>( categoryCombo.getOptionCombos() );
             verify( nonEmptyLists( categoryOptionCombos ) == 1, "Category option combos size must be larger than 0" );
         }
 
@@ -371,36 +405,66 @@
         allIndicators.addAll( dataElements );
         allIndicators.addAll( indicators );
         allIndicators.addAll( dataSets );
-                
+
         allPeriods.addAll( periods );
         allPeriods.addAll( relativePeriods );
         allPeriods = removeDuplicates( allPeriods );
-        
-        Collections.sort( allPeriods, new AscendingPeriodComparator() ); // Sort periods ascending
+
+        Collections.sort( allPeriods, new AscendingPeriodComparator() ); // Sort
+        // periods
+        // ascending
         setNames( allPeriods ); // Set names on periods
-        
+
         allUnits.addAll( units );
         allUnits.addAll( relativeUnits );
         allUnits = removeDuplicates( allUnits );
 
         columns = new CombinationGenerator<NameableObject>( getArrays( true ) ).getCombinations();
         rows = new CombinationGenerator<NameableObject>( getArrays( false ) ).getCombinations();
-        
+
         addIfEmpty( columns ); // Allow for all or none crosstab dimensions
         addIfEmpty( rows );
-        
+
         add( indexColumns, INDICATOR_ID, doIndicators );
         add( indexColumns, PERIOD_ID, doPeriods );
         add( indexColumns, ORGANISATIONUNIT_ID, doUnits );
         add( indexNameColumns, INDICATOR_NAME, doIndicators );
         add( indexNameColumns, PERIOD_NAME, doPeriods );
-        add( indexNameColumns, ORGANISATIONUNIT_NAME, doUnits );        
+        add( indexNameColumns, ORGANISATIONUNIT_NAME, doUnits );
     }
 
     // -------------------------------------------------------------------------
     // Public methods
     // -------------------------------------------------------------------------
-        
+
+    public void addReportTableGroup( ReportTableGroup group )
+    {
+        groups.add( group );
+        group.getMembers().add( this );
+    }
+
+    public void removeReportTableGroup( ReportTableGroup group )
+    {
+        groups.remove( group );
+        group.getMembers().remove( this );
+    }
+
+    public void updateReportTableGroups( Set<ReportTableGroup> updates )
+    {
+        for ( ReportTableGroup group : new HashSet<ReportTableGroup>( groups ) )
+        {
+            if ( !updates.contains( group ) )
+            {
+                removeReportTableGroup( group );
+            }
+        }
+
+        for ( ReportTableGroup group : updates )
+        {
+            addReportTableGroup( group );
+        }
+    }
+
     /**
      * Indicates whether this ReportTable is multi-dimensional.
      */
@@ -408,7 +472,7 @@
     {
         return categoryCombo != null;
     }
-        
+
     /**
      * Indicates whether a total column should be included.
      */
@@ -416,7 +480,7 @@
     {
         return !isDoIndicators() && !isDoPeriods() && !isDoUnits() && isDimensional();
     }
-    
+
     /**
      * Indicates whether subtotal columns should be included. The category combo
      * of the report table must have more than one category if subtotal columns
@@ -426,45 +490,52 @@
     {
         return doTotal() && categoryCombo.getCategories() != null && categoryCombo.getCategories().size() > 1;
     }
-    
+
     /**
-     * Generates a pretty column name based on short-names of the argument objects. 
-     * Null arguments are ignored in the name.
+     * Generates a pretty column name based on short-names of the argument
+     * objects. Null arguments are ignored in the name.
      */
     public static String getPrettyColumnName( List<NameableObject> objects )
     {
         StringBuffer buffer = new StringBuffer();
-        
+
         for ( NameableObject object : objects )
         {
-            buffer.append( object != null ? ( object.getShortName() + SPACE ) : EMPTY );
+            buffer.append( object != null ? (object.getShortName() + SPACE) : EMPTY );
         }
-        
+
         return buffer.length() > 0 ? buffer.substring( 0, buffer.lastIndexOf( SPACE ) ) : TOTAL_COLUMN_PRETTY_NAME;
     }
-    
+
     /**
-     * Generates a column name based on short-names of the argument objects. Null 
-     * arguments are ignored in the name.
+     * Generates a column name based on short-names of the argument objects.
+     * Null arguments are ignored in the name.
      */
     public static String getColumnName( List<NameableObject> objects )
     {
         StringBuffer buffer = new StringBuffer();
-        
+
         for ( NameableObject object : objects )
         {
             if ( object != null && object instanceof Period )
             {
-                buffer.append( object.getName() + SEPARATOR ); // Relative periods must have static names when crosstabbed - which are set on name property
+                buffer.append( object.getName() + SEPARATOR ); // Relative
+                // periods must
+                // have static
+                // names when
+                // crosstabbed -
+                // which are set
+                // on name
+                // property
             }
             else
             {
-                buffer.append( object != null ? ( object.getShortName() + SEPARATOR ) : EMPTY );
+                buffer.append( object != null ? (object.getShortName() + SEPARATOR) : EMPTY );
             }
         }
 
         String column = columnEncode( buffer.toString() );
-        
+
         return column.length() > 0 ? column.substring( 0, column.lastIndexOf( SEPARATOR ) ) : TOTAL_COLUMN_NAME;
     }
 
@@ -476,7 +547,7 @@
     {
         return getIdentifier( objects, new ArrayList<NameableObject>() );
     }
-    
+
     /**
      * Generates a grid identifier based on the internal identifiers of the
      * argument objects.
@@ -484,17 +555,17 @@
     public static String getIdentifier( List<? extends NameableObject> objects1, List<? extends NameableObject> objects2 )
     {
         List<String> identifiers = new ArrayList<String>();
-        
+
         for ( NameableObject object : objects1 )
         {
             identifiers.add( getIdentifier( object.getClass(), object.getId() ) );
         }
-        
+
         for ( NameableObject object : objects2 )
         {
             identifiers.add( getIdentifier( object.getClass(), object.getId() ) );
         }
-        
+
         return getIdentifier( identifiers.toArray( SRT ) );
     }
 
@@ -504,29 +575,30 @@
     public static String getIdentifier( List<NameableObject> objects, Class<? extends NameableObject> clazz, int id )
     {
         List<String> identifiers = new ArrayList<String>();
-        
+
         for ( NameableObject object : objects )
         {
             identifiers.add( getIdentifier( object.getClass(), object.getId() ) );
         }
-        
+
         identifiers.add( getIdentifier( clazz, id ) );
-        
-        return getIdentifier( identifiers.toArray( SRT  ) );
+
+        return getIdentifier( identifiers.toArray( SRT ) );
     }
-    
+
     /**
      * Generates a grid column identifier based on the argument identifiers.
      */
     public static String getIdentifier( String... identifiers )
     {
         List<String> ids = Arrays.asList( identifiers );
-        
-        Collections.sort( ids ); // Sort to remove the significance of the order
-        
+
+        Collections.sort( ids ); // Sort to remove the significance of the
+        // order
+
         return StringUtils.join( ids, SEPARATOR );
     }
-    
+
     /**
      * Returns a grid identifier based on the argument class and id.
      */
@@ -534,7 +606,7 @@
     {
         return CLASS_ID_MAP.get( clazz ) + id;
     }
-    
+
     /**
      * Indicates whether the report table contains data elements.
      */
@@ -542,7 +614,7 @@
     {
         return dataElements != null && dataElements.size() > 0;
     }
-    
+
     /**
      * Indicates whether the report table contains indicators.
      */
@@ -550,7 +622,7 @@
     {
         return indicators != null && indicators.size() > 0;
     }
-    
+
     /**
      * Indicates whether the report table contains data sets.
      */
@@ -572,10 +644,10 @@
             string = string.length() > 255 ? string.substring( 0, 255 ) : string;
             string = string.toLowerCase();
         }
-        
+
         return string;
     }
-    
+
     /**
      * Returns null-safe sort order, none if null.
      */
@@ -583,7 +655,7 @@
     {
         return sortOrder != null ? sortOrder : NONE;
     }
-    
+
     /**
      * Returns null-safe top limit, 0 if null;
      */
@@ -591,7 +663,7 @@
     {
         return topLimit != null ? topLimit : 0;
     }
-    
+
     // -------------------------------------------------------------------------
     // Supportive methods
     // -------------------------------------------------------------------------
@@ -599,27 +671,27 @@
     private NameableObject[][] getArrays( boolean crosstab )
     {
         List<NameableObject[]> arrays = new ArrayList<NameableObject[]>();
-        
-        if ( ( doIndicators && crosstab ) || ( !doIndicators && !crosstab ) )
+
+        if ( (doIndicators && crosstab) || (!doIndicators && !crosstab) )
         {
             arrays.add( allIndicators.toArray( IRT ) );
         }
-        
-        if ( ( doPeriods && crosstab ) || ( !doPeriods && !crosstab ) )
+
+        if ( (doPeriods && crosstab) || (!doPeriods && !crosstab) )
         {
             arrays.add( allPeriods.toArray( IRT ) );
         }
-        
-        if ( ( doUnits && crosstab ) || ( !doUnits && !crosstab ) )
+
+        if ( (doUnits && crosstab) || (!doUnits && !crosstab) )
         {
             arrays.add( allUnits.toArray( IRT ) );
         }
-        
+
         if ( isDimensional() && crosstab ) // Must be crosstab if exists
         {
             arrays.add( categoryOptionCombos.toArray( IRT ) );
         }
-        
+
         return arrays.toArray( new NameableObject[0][] );
     }
 
@@ -633,14 +705,14 @@
             list.add( Arrays.asList( new NameableObject[0] ) );
         }
     }
-    
+
     /**
      * Returns the number of empty lists among the argument lists.
      */
     private static int nonEmptyLists( List<?>... lists )
     {
         int nonEmpty = 0;
-        
+
         for ( List<?> list : lists )
         {
             if ( list != null && list.size() > 0 )
@@ -648,13 +720,13 @@
                 ++nonEmpty;
             }
         }
-        
+
         return nonEmpty;
     }
-    
+
     /**
-     * Sets the name and short name properties on the given Periods which don't have
-     * the name property already set.
+     * Sets the name and short name properties on the given Periods which don't
+     * have the name property already set.
      */
     private void setNames( List<Period> periods )
     {
@@ -662,10 +734,15 @@
         {
             if ( period.getName() == null ) // Crosstabulated relative periods
             {
-                period.setName( i18nFormat.formatPeriod( period ) ); // Static periods + indexed relative periods
-                period.setShortName( i18nFormat.formatPeriod( period ) );                
+                period.setName( i18nFormat.formatPeriod( period ) ); // Static
+                // periods
+                // +
+                // indexed
+                // relative
+                // periods
+                period.setShortName( i18nFormat.formatPeriod( period ) );
             }
-        }        
+        }
     }
 
     /**
@@ -678,7 +755,7 @@
             list.add( object );
         }
     }
-    
+
     /**
      * Removes duplicates from the given list while maintaining the order.
      */
@@ -686,7 +763,7 @@
     {
         final List<T> temp = new ArrayList<T>( list );
         list.clear();
-        
+
         for ( T object : temp )
         {
             if ( !list.contains( object ) )
@@ -694,10 +771,10 @@
                 list.add( object );
             }
         }
-        
+
         return list;
     }
-    
+
     /**
      * Supportive method.
      */
@@ -706,9 +783,9 @@
         if ( !expression )
         {
             throw new IllegalStateException( falseMessage );
-        }   
+        }
     }
-    
+
     // -------------------------------------------------------------------------
     // Equals and hashCode
     // -------------------------------------------------------------------------
@@ -717,11 +794,11 @@
     public int hashCode()
     {
         final int PRIME = 31;
-        
+
         int result = 1;
-        
-        result = PRIME * result + ( ( name == null ) ? 0 : name.hashCode() );
-        
+
+        result = PRIME * result + ((name == null) ? 0 : name.hashCode());
+
         return result;
     }
 
@@ -732,22 +809,22 @@
         {
             return true;
         }
-        
+
         if ( object == null )
         {
             return false;
         }
-        
+
         if ( getClass() != object.getClass() )
         {
             return false;
         }
-        
+
         final ReportTable other = (ReportTable) object;
-        
+
         return name.equals( other.getName() );
     }
-    
+
     // -------------------------------------------------------------------------
     // Get- and set-methods for persisted properties
     // -------------------------------------------------------------------------
@@ -781,7 +858,7 @@
     {
         this.categoryOptionCombos = categoryOptionCombos;
     }
-    
+
     public List<Indicator> getIndicators()
     {
         return indicators;
@@ -822,6 +899,16 @@
         this.units = units;
     }
 
+    public List<ReportTableGroup> getGroups()
+    {
+        return groups;
+    }
+
+    public void setGroups( List<ReportTableGroup> groups )
+    {
+        this.groups = groups;
+    }
+
     public DataElementCategoryCombo getCategoryCombo()
     {
         return categoryCombo;
@@ -935,7 +1022,7 @@
     {
         return allUnits;
     }
-    
+
     public I18nFormat getI18nFormat()
     {
         return i18nFormat;
@@ -955,7 +1042,7 @@
     {
         this.reportingMonthName = reportingMonthName;
     }
-    
+
     public String getOrganisationUnitName()
     {
         return organisationUnitName;

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableGroup.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableGroup.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableGroup.java	2011-07-21 03:29:35 +0000
@@ -0,0 +1,146 @@
+package org.hisp.dhis.reporttable;
+
+/*
+ * Copyright (c) 2004-2011, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hisp.dhis.common.AbstractIdentifiableObject;
+
+/**
+ * @author Dang Duy Hieu
+ * @version $Id$
+ */
+public class ReportTableGroup
+    extends AbstractIdentifiableObject
+{
+    /**
+     * Determines if a de-serialized file is compatible with this class.
+     */
+    private static final long serialVersionUID = -1L;
+
+    private Set<ReportTable> members = new HashSet<ReportTable>();
+    
+    // -------------------------------------------------------------------------
+    // Constructors
+    // -------------------------------------------------------------------------
+
+    public ReportTableGroup()
+    {
+    }
+
+    public ReportTableGroup( String name )
+    {
+        this.name = name;
+    }
+
+    // -------------------------------------------------------------------------
+    // Logic
+    // -------------------------------------------------------------------------
+
+    public void addReportTable( ReportTable reportTable )
+    {
+        members.add( reportTable );
+        reportTable.getGroups().add( this );
+    }
+    
+    public void removeReportTable( ReportTable reportTable )
+    {
+        members.remove( reportTable );
+        reportTable.getGroups().remove( this );
+    }
+    
+    public void updateReportTables( Set<ReportTable> updates )
+    {
+        for ( ReportTable reportTable : new HashSet<ReportTable>( members ) )
+        {
+            if ( !updates.contains( reportTable ) )
+            {
+                removeReportTable( reportTable );
+            }
+        }
+        
+        for ( ReportTable reportTable : updates )
+        {
+            addReportTable( reportTable );
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // hashCode and equals
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int hashCode()
+    {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( !(o instanceof ReportTableGroup) )
+        {
+            return false;
+        }
+
+        final ReportTableGroup other = (ReportTableGroup) o;
+
+        return name.equals( other.getName() );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + name + "]";
+    }
+
+    // -------------------------------------------------------------------------
+    // Getters and setters
+    // -------------------------------------------------------------------------
+
+    public Set<ReportTable> getMembers()
+    {
+        return members;
+    }
+
+    public void setMembers( Set<ReportTable> members )
+    {
+        this.members = members;
+    }
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableService.java	2011-04-06 16:03:34 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTableService.java	2011-07-21 03:29:35 +0000
@@ -139,4 +139,32 @@
     int getReportTableCount();
     
     int getReportTableCountByName( String name );
+    
+ // -------------------------------------------------------------------------
+    // ReportTableGroup
+    // -------------------------------------------------------------------------
+
+    int addReportTableGroup( ReportTableGroup reportTableGroup );
+
+    void updateReportTableGroup( ReportTableGroup reportTableGroup );
+
+    void deleteReportTableGroup( ReportTableGroup reportTableGroup );
+
+    ReportTableGroup getReportTableGroup( int id );
+
+    ReportTableGroup getReportTableGroupByName( String name );
+
+    Collection<ReportTableGroup> getAllReportTableGroups();
+
+    Collection<ReportTableGroup> getReportTableGroups( final Collection<Integer> identifiers );
+
+    Collection<ReportTableGroup> getGroupsContainingReportTable( ReportTable reportTable );
+
+    int getReportTableGroupCount();
+
+    int getReportTableGroupCountByName( String name );
+
+    Collection<ReportTableGroup> getReportTableGroupsBetween( int first, int max );
+
+    Collection<ReportTableGroup> getReportTableGroupsBetweenByName( String name, int first, int max );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java	2011-07-07 13:29:56 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java	2011-07-21 03:29:35 +0000
@@ -27,12 +27,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import static org.hisp.dhis.chart.Chart.DIMENSION_DATAELEMENT_PERIOD;
 import static org.hisp.dhis.chart.Chart.DIMENSION_INDICATOR_PERIOD;
+import static org.hisp.dhis.chart.Chart.DIMENSION_ORGANISATIONUNIT_DATAELEMENT;
 import static org.hisp.dhis.chart.Chart.DIMENSION_ORGANISATIONUNIT_INDICATOR;
+import static org.hisp.dhis.chart.Chart.DIMENSION_PERIOD_DATAELEMENT;
 import static org.hisp.dhis.chart.Chart.DIMENSION_PERIOD_INDICATOR;
-import static org.hisp.dhis.chart.Chart.DIMENSION_DATAELEMENT_PERIOD;
-import static org.hisp.dhis.chart.Chart.DIMENSION_ORGANISATIONUNIT_DATAELEMENT;
-import static org.hisp.dhis.chart.Chart.DIMENSION_PERIOD_DATAELEMENT;
 import static org.hisp.dhis.chart.Chart.SIZE_NORMAL;
 import static org.hisp.dhis.chart.Chart.TYPE_BAR;
 import static org.hisp.dhis.chart.Chart.TYPE_BAR3D;
@@ -50,6 +50,7 @@
 import java.awt.Font;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -62,8 +63,10 @@
 import org.hisp.dhis.aggregation.AggregatedDataValueService;
 import org.hisp.dhis.aggregation.AggregationService;
 import org.hisp.dhis.chart.Chart;
+import org.hisp.dhis.chart.ChartGroup;
 import org.hisp.dhis.chart.ChartService;
 import org.hisp.dhis.chart.ChartStore;
+import org.hisp.dhis.common.GenericIdentifiableObjectStore;
 import org.hisp.dhis.common.NameableObject;
 import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
@@ -191,6 +194,13 @@
         this.currentUserService = currentUserService;
     }
 
+    private GenericIdentifiableObjectStore<ChartGroup> chartGroupStore;
+
+    public void setChartGroupStore( GenericIdentifiableObjectStore<ChartGroup> chartGroupStore )
+    {
+        this.chartGroupStore = chartGroupStore;
+    }
+
     // -------------------------------------------------------------------------
     // ChartService implementation
     // -------------------------------------------------------------------------
@@ -326,7 +336,8 @@
         // Interpolation DataSet
         // ---------------------------------------------------------------------
 
-        if ( x.size() >= 3 ) // minimum 3 data points required for interpolation
+        if ( x.size() >= 3 ) // minimum 3 data points required for
+        // interpolation
         {
             periodCount = 0;
 
@@ -369,6 +380,92 @@
     }
 
     // -------------------------------------------------------------------------
+    // ChartGroup
+    // -------------------------------------------------------------------------
+
+    public int addChartGroup( ChartGroup chartGroup )
+    {
+        return chartGroupStore.save( chartGroup );
+    }
+
+    public void updateChartGroup( ChartGroup chartGroup )
+    {
+        chartGroupStore.update( chartGroup );
+    }
+
+    public void deleteChartGroup( ChartGroup chartGroup )
+    {
+        chartGroupStore.delete( chartGroup );
+    }
+
+    public ChartGroup getChartGroup( int id )
+    {
+        return chartGroupStore.get( id );
+    }
+
+    public ChartGroup getChartGroupByName( String name )
+    {
+        return chartGroupStore.getByName( name );
+    }
+
+    public Collection<ChartGroup> getAllChartGroups()
+    {
+        return chartGroupStore.getAll();
+    }
+
+    public Collection<ChartGroup> getChartGroups( final Collection<Integer> identifiers )
+    {
+        Collection<ChartGroup> groups = getAllChartGroups();
+
+        return identifiers == null ? groups : FilterUtils.filter( groups, new Filter<ChartGroup>()
+        {
+            public boolean retain( ChartGroup object )
+            {
+                return identifiers.contains( object.getId() );
+            }
+        } );
+    }
+
+    public Collection<ChartGroup> getGroupsContainingChart( Chart chart )
+    {
+        Collection<ChartGroup> groups = getAllChartGroups();
+
+        Iterator<ChartGroup> iterator = groups.iterator();
+
+        while ( iterator.hasNext() )
+        {
+            ChartGroup group = iterator.next();
+
+            if ( !group.getMembers().contains( chart ) )
+            {
+                iterator.remove();
+            }
+        }
+
+        return groups;
+    }
+
+    public int getChartGroupCount()
+    {
+        return chartGroupStore.getCount();
+    }
+
+    public int getChartGroupCountByName( String name )
+    {
+        return chartGroupStore.getCountByName( name );
+    }
+
+    public Collection<ChartGroup> getChartGroupsBetween( int first, int max )
+    {
+        return chartGroupStore.getBetween( first, max );
+    }
+
+    public Collection<ChartGroup> getChartGroupsBetweenByName( String name, int first, int max )
+    {
+        return chartGroupStore.getBetweenByName( name, first, max );
+    }
+
+    // -------------------------------------------------------------------------
     // Supportive methods
     // -------------------------------------------------------------------------
 
@@ -658,15 +755,15 @@
                         if ( isIndicatorChart )
                         {
                             value = aggregationStrategy.equals( AGGREGATION_STRATEGY_REAL_TIME ) ? aggregationService
-                                .getAggregatedIndicatorValue( indicators.get( i ), period.getStartDate(),
-                                    period.getEndDate(), selectedOrganisationUnit ) : aggregatedDataValueService
+                                .getAggregatedIndicatorValue( indicators.get( i ), period.getStartDate(), period
+                                    .getEndDate(), selectedOrganisationUnit ) : aggregatedDataValueService
                                 .getAggregatedValue( indicators.get( i ), period, selectedOrganisationUnit );
                         }
                         else if ( isDataElementChart )
                         {
                             value = aggregationStrategy.equals( AGGREGATION_STRATEGY_REAL_TIME ) ? aggregationService
-                                .getAggregatedDataValue( dataElements.get( i ), null, period.getStartDate(),
-                                    period.getEndDate(), selectedOrganisationUnit ) : aggregatedDataValueService
+                                .getAggregatedDataValue( dataElements.get( i ), null, period.getStartDate(), period
+                                    .getEndDate(), selectedOrganisationUnit ) : aggregatedDataValueService
                                 .getAggregatedValue( dataElements.get( i ), period, selectedOrganisationUnit );
                         }
 

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/report/impl/DefaultReportService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/report/impl/DefaultReportService.java	2010-07-28 13:44:59 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/report/impl/DefaultReportService.java	2011-07-21 03:29:35 +0000
@@ -28,9 +28,11 @@
  */
 
 import java.util.Collection;
+import java.util.Iterator;
 
 import org.hisp.dhis.common.GenericIdentifiableObjectStore;
 import org.hisp.dhis.report.Report;
+import org.hisp.dhis.report.ReportGroup;
 import org.hisp.dhis.report.ReportService;
 import org.hisp.dhis.system.util.Filter;
 import org.hisp.dhis.system.util.FilterUtils;
@@ -44,6 +46,10 @@
 public class DefaultReportService
     implements ReportService
 {
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
     private GenericIdentifiableObjectStore<Report> reportStore;
 
     public void setReportStore( GenericIdentifiableObjectStore<Report> reportStore )
@@ -51,6 +57,17 @@
         this.reportStore = reportStore;
     }
 
+    private GenericIdentifiableObjectStore<ReportGroup> reportGroupStore;
+
+    public void setReportGroupStore( GenericIdentifiableObjectStore<ReportGroup> reportGroupStore )
+    {
+        this.reportGroupStore = reportGroupStore;
+    }
+
+    // -------------------------------------------------------------------------
+    // Implements
+    // -------------------------------------------------------------------------
+
     public int saveReport( Report report )
     {
         return reportStore.save( report );
@@ -70,7 +87,7 @@
     {
         return reportStore.get( id );
     }
-    
+
     public Report getReportByName( String name )
     {
         return reportStore.getByName( name );
@@ -88,4 +105,90 @@
             }
         } );
     }
+
+    // -------------------------------------------------------------------------
+    // ReportGroup
+    // -------------------------------------------------------------------------
+
+    public int addReportGroup( ReportGroup reportGroup )
+    {
+        return reportGroupStore.save( reportGroup );
+    }
+
+    public void updateReportGroup( ReportGroup reportGroup )
+    {
+        reportGroupStore.update( reportGroup );
+    }
+
+    public void deleteReportGroup( ReportGroup reportGroup )
+    {
+        reportGroupStore.delete( reportGroup );
+    }
+
+    public ReportGroup getReportGroup( int id )
+    {
+        return reportGroupStore.get( id );
+    }
+
+    public ReportGroup getReportGroupByName( String name )
+    {
+        return reportGroupStore.getByName( name );
+    }
+
+    public Collection<ReportGroup> getAllReportGroups()
+    {
+        return reportGroupStore.getAll();
+    }
+
+    public Collection<ReportGroup> getReportGroups( final Collection<Integer> identifiers )
+    {
+        Collection<ReportGroup> groups = getAllReportGroups();
+
+        return identifiers == null ? groups : FilterUtils.filter( groups, new Filter<ReportGroup>()
+        {
+            public boolean retain( ReportGroup object )
+            {
+                return identifiers.contains( object.getId() );
+            }
+        } );
+    }
+
+    public Collection<ReportGroup> getGroupsContainingReport( Report report )
+    {
+        Collection<ReportGroup> groups = getAllReportGroups();
+
+        Iterator<ReportGroup> iterator = groups.iterator();
+
+        while ( iterator.hasNext() )
+        {
+            ReportGroup group = iterator.next();
+
+            if ( !group.getMembers().contains( report ) )
+            {
+                iterator.remove();
+            }
+        }
+
+        return groups;
+    }
+
+    public int getReportGroupCount()
+    {
+        return reportGroupStore.getCount();
+    }
+
+    public int getReportGroupCountByName( String name )
+    {
+        return reportGroupStore.getCountByName( name );
+    }
+
+    public Collection<ReportGroup> getReportGroupsBetween( int first, int max )
+    {
+        return reportGroupStore.getBetween( first, max );
+    }
+
+    public Collection<ReportGroup> getReportGroupsBetweenByName( String name, int first, int max )
+    {
+        return reportGroupStore.getBetweenByName( name, first, max );
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java	2011-04-06 16:03:34 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java	2011-07-21 03:29:35 +0000
@@ -42,6 +42,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -66,6 +67,7 @@
 import org.hisp.dhis.period.PeriodService;
 import org.hisp.dhis.report.ReportService;
 import org.hisp.dhis.reporttable.ReportTable;
+import org.hisp.dhis.reporttable.ReportTableGroup;
 import org.hisp.dhis.reporttable.ReportTableService;
 import org.hisp.dhis.reporttable.jdbc.ReportTableManager;
 import org.hisp.dhis.system.grid.ListGrid;
@@ -77,32 +79,41 @@
  * @author Lars Helge Overland
  * @version $Id$
  */
+@Transactional
 public class DefaultReportTableService
     implements ReportTableService
 {
     private static final Log log = LogFactory.getLog( DefaultReportTableService.class );
-    
+
     private static final String YES = "Yes";
+
     private static final String NO = "No";
-    
+
     // ---------------------------------------------------------------------
     // Dependencies
     // ---------------------------------------------------------------------
 
     private ReportTableManager reportTableManager;
-    
+
     public void setReportTableManager( ReportTableManager reportTableManager )
     {
         this.reportTableManager = reportTableManager;
     }
 
     private GenericIdentifiableObjectStore<ReportTable> reportTableStore;
-    
+
     public void setReportTableStore( GenericIdentifiableObjectStore<ReportTable> reportTableStore )
     {
         this.reportTableStore = reportTableStore;
     }
 
+    private GenericIdentifiableObjectStore<ReportTableGroup> reportTableGroupStore;
+
+    public void setReportTableGroupStore( GenericIdentifiableObjectStore<ReportTableGroup> reportTableGroupStore )
+    {
+        this.reportTableGroupStore = reportTableGroupStore;
+    }
+
     protected ReportService reportService;
 
     public void setReportService( ReportService reportService )
@@ -123,9 +134,9 @@
     {
         this.organisationUnitService = organisationUnitService;
     }
-    
+
     private DataMartService dataMartService;
-    
+
     public void setDataMartService( DataMartService dataMartService )
     {
         this.dataMartService = dataMartService;
@@ -142,34 +153,32 @@
     // ReportTableService implementation
     // -------------------------------------------------------------------------
 
-    @Transactional
-    public void populateReportTableDataMart( int id, String mode, Integer reportingPeriod, Integer organisationUnitId, I18nFormat format )
+    public void populateReportTableDataMart( int id, String mode, Integer reportingPeriod, Integer organisationUnitId,
+        I18nFormat format )
     {
         ReportTable reportTable = getReportTable( id, mode );
-        
+
         reportTable = initDynamicMetaObjects( reportTable, reportingPeriod, organisationUnitId, format );
-        
+
         if ( reportTable.hasDataElements() || reportTable.hasIndicators() )
         {
-            dataMartService.export( getIdentifiers( DataElement.class, reportTable.getDataElements() ),
-                getIdentifiers( Indicator.class, reportTable.getIndicators() ),
-                getIdentifiers( Period.class, reportTable.getAllPeriods() ),
-                getIdentifiers( OrganisationUnit.class, reportTable.getAllUnits() ) );
+            dataMartService.export( getIdentifiers( DataElement.class, reportTable.getDataElements() ), getIdentifiers(
+                Indicator.class, reportTable.getIndicators() ), getIdentifiers( Period.class, reportTable
+                .getAllPeriods() ), getIdentifiers( OrganisationUnit.class, reportTable.getAllUnits() ) );
         }
-        
+
         if ( reportTable.hasDataSets() )
         {
             completenessService.exportDataSetCompleteness( getIdentifiers( DataSet.class, reportTable.getDataSets() ),
-                getIdentifiers( Period.class, reportTable.getAllPeriods() ),
-                getIdentifiers( OrganisationUnit.class, reportTable.getAllUnits() ) );
+                getIdentifiers( Period.class, reportTable.getAllPeriods() ), getIdentifiers( OrganisationUnit.class,
+                    reportTable.getAllUnits() ) );
         }
     }
-    
-    @Transactional
+
     public Grid getReportTableGrid( int id, I18nFormat format, Integer reportingPeriod, Integer organisationUnitId )
     {
         ReportTable reportTable = getReportTable( id );
-        
+
         reportTable = initDynamicMetaObjects( reportTable, reportingPeriod, organisationUnitId, format );
 
         return getGrid( reportTable );
@@ -185,95 +194,170 @@
         {
             return reportService.getReport( id ).getReportTable();
         }
-        
+
         return null;
     }
-    
+
     // -------------------------------------------------------------------------
     // Persistence
     // -------------------------------------------------------------------------
 
-    @Transactional
     public int saveReportTable( ReportTable reportTable )
     {
         return reportTableStore.save( reportTable );
     }
 
-    @Transactional
     public void updateReportTable( ReportTable reportTable )
     {
         reportTableStore.update( reportTable );
     }
 
-    @Transactional
     public void deleteReportTable( ReportTable reportTable )
     {
         reportTableStore.delete( reportTable );
     }
 
-    @Transactional
     public ReportTable getReportTable( int id )
     {
         return reportTableStore.get( id );
     }
 
-    @Transactional
     public Collection<ReportTable> getReportTables( final Collection<Integer> identifiers )
     {
         Collection<ReportTable> objects = getAllReportTables();
-        
+
         return identifiers == null ? objects : FilterUtils.filter( objects, new Filter<ReportTable>()
+        {
+            public boolean retain( ReportTable object )
             {
-                public boolean retain( ReportTable object )
-                {
-                    return identifiers.contains( object.getId() );
-                }
-            } );
+                return identifiers.contains( object.getId() );
+            }
+        } );
     }
 
-    @Transactional
     public Collection<ReportTable> getAllReportTables()
     {
         return reportTableStore.getAll();
     }
 
-    @Transactional
     public ReportTable getReportTableByName( String name )
     {
         return reportTableStore.getByName( name );
     }
 
-    @Transactional
     public Collection<ReportTable> getReportTablesBetweenByName( String name, int first, int max )
     {
         return reportTableStore.getBetweenByName( name, first, max );
     }
 
-    @Transactional
     public int getReportTableCount()
     {
         return reportTableStore.getCount();
     }
 
-    @Transactional
     public int getReportTableCountByName( String name )
     {
         return reportTableStore.getCountByName( name );
     }
 
-    @Transactional
     public Collection<ReportTable> getReportTablesBetween( int first, int max )
     {
         return reportTableStore.getBetween( first, max );
     }
-    
+
+    // -------------------------------------------------------------------------
+    // ReportTableGroup
+    // -------------------------------------------------------------------------
+
+    public int addReportTableGroup( ReportTableGroup reportTableGroup )
+    {
+        return reportTableGroupStore.save( reportTableGroup );
+    }
+
+    public void updateReportTableGroup( ReportTableGroup reportTableGroup )
+    {
+        reportTableGroupStore.update( reportTableGroup );
+    }
+
+    public void deleteReportTableGroup( ReportTableGroup reportTableGroup )
+    {
+        reportTableGroupStore.delete( reportTableGroup );
+    }
+
+    public ReportTableGroup getReportTableGroup( int id )
+    {
+        return reportTableGroupStore.get( id );
+    }
+
+    public ReportTableGroup getReportTableGroupByName( String name )
+    {
+        return reportTableGroupStore.getByName( name );
+    }
+
+    public Collection<ReportTableGroup> getAllReportTableGroups()
+    {
+        return reportTableGroupStore.getAll();
+    }
+
+    public Collection<ReportTableGroup> getReportTableGroups( final Collection<Integer> identifiers )
+    {
+        Collection<ReportTableGroup> groups = getAllReportTableGroups();
+
+        return identifiers == null ? groups : FilterUtils.filter( groups, new Filter<ReportTableGroup>()
+        {
+            public boolean retain( ReportTableGroup object )
+            {
+                return identifiers.contains( object.getId() );
+            }
+        } );
+    }
+
+    public Collection<ReportTableGroup> getGroupsContainingReportTable( ReportTable reportTable )
+    {
+        Collection<ReportTableGroup> groups = getAllReportTableGroups();
+
+        Iterator<ReportTableGroup> iterator = groups.iterator();
+
+        while ( iterator.hasNext() )
+        {
+            ReportTableGroup group = iterator.next();
+
+            if ( !group.getMembers().contains( reportTable ) )
+            {
+                iterator.remove();
+            }
+        }
+
+        return groups;
+    }
+
+    public int getReportTableGroupCount()
+    {
+        return reportTableGroupStore.getCount();
+    }
+
+    public int getReportTableGroupCountByName( String name )
+    {
+        return reportTableGroupStore.getCountByName( name );
+    }
+
+    public Collection<ReportTableGroup> getReportTableGroupsBetween( int first, int max )
+    {
+        return reportTableGroupStore.getBetween( first, max );
+    }
+
+    public Collection<ReportTableGroup> getReportTableGroupsBetweenByName( String name, int first, int max )
+    {
+        return reportTableGroupStore.getBetweenByName( name, first, max );
+    }
+
     // -------------------------------------------------------------------------
     // Supportive methods
     // -------------------------------------------------------------------------
 
     /**
-     * Populates the report table with dynamic meta objects originating from report
-     * table parameters.
+     * Populates the report table with dynamic meta objects originating from
+     * report table parameters.
      * 
      * @param reportTable the report table.
      * @param reportingPeriod the reporting period number.
@@ -281,7 +365,7 @@
      * @param format the I18n format.
      * @return a report table.
      */
-    private ReportTable initDynamicMetaObjects( ReportTable reportTable, Integer reportingPeriod, 
+    private ReportTable initDynamicMetaObjects( ReportTable reportTable, Integer reportingPeriod,
         Integer organisationUnitId, I18nFormat format )
     {
         // ---------------------------------------------------------------------
@@ -289,17 +373,20 @@
         // ---------------------------------------------------------------------
 
         if ( reportTable.getReportParams() != null && reportTable.getReportParams().isParamReportingMonth() )
-        {             
-            reportTable.setRelativePeriods( periodService.reloadPeriods( reportTable.getRelatives().getRelativePeriods( reportingPeriod, format, !reportTable.isDoPeriods() ) ) );                                
-            reportTable.setReportingMonthName( reportTable.getRelatives().getReportingMonthName( reportingPeriod, format ) );
-            
+        {
+            reportTable.setRelativePeriods( periodService.reloadPeriods( reportTable.getRelatives().getRelativePeriods(
+                reportingPeriod, format, !reportTable.isDoPeriods() ) ) );
+            reportTable.setReportingMonthName( reportTable.getRelatives().getReportingMonthName( reportingPeriod,
+                format ) );
+
             log.info( "Reporting period date from report param: " + reportTable.getReportingMonthName() );
         }
         else
         {
-            reportTable.setRelativePeriods( periodService.reloadPeriods( reportTable.getRelatives().getRelativePeriods( 1, format, !reportTable.isDoPeriods() ) ) );                
+            reportTable.setRelativePeriods( periodService.reloadPeriods( reportTable.getRelatives().getRelativePeriods(
+                1, format, !reportTable.isDoPeriods() ) ) );
             reportTable.setReportingMonthName( reportTable.getRelatives().getReportingMonthName( 1, format ) );
-            
+
             log.info( "Reporting period date default: " + reportTable.getReportingMonthName() );
         }
 
@@ -307,14 +394,16 @@
         // Grand parent organisation unit report parameter
         // ---------------------------------------------------------------------
 
-        if ( reportTable.getReportParams() != null && reportTable.getReportParams().isParamGrandParentOrganisationUnit() )
+        if ( reportTable.getReportParams() != null
+            && reportTable.getReportParams().isParamGrandParentOrganisationUnit() )
         {
             OrganisationUnit organisationUnit = organisationUnitService.getOrganisationUnit( organisationUnitId );
             organisationUnit.setCurrentParent( true );
-            reportTable.getRelativeUnits().addAll( new ArrayList<OrganisationUnit>( organisationUnit.getGrandChildren() ) );
+            reportTable.getRelativeUnits().addAll(
+                new ArrayList<OrganisationUnit>( organisationUnit.getGrandChildren() ) );
             reportTable.getRelativeUnits().add( organisationUnit );
             reportTable.setOrganisationUnitName( organisationUnit.getName() );
-            
+
             log.info( "Grand parent organisation unit: " + organisationUnit.getName() );
         }
 
@@ -329,7 +418,7 @@
             reportTable.getRelativeUnits().addAll( new ArrayList<OrganisationUnit>( organisationUnit.getChildren() ) );
             reportTable.getRelativeUnits().add( organisationUnit );
             reportTable.setOrganisationUnitName( organisationUnit.getName() );
-            
+
             log.info( "Parent organisation unit: " + organisationUnit.getName() );
         }
 
@@ -339,10 +428,10 @@
 
         if ( reportTable.getReportParams() != null && reportTable.getReportParams().isParamOrganisationUnit() )
         {
-            OrganisationUnit organisationUnit = organisationUnitService.getOrganisationUnit( organisationUnitId );            
+            OrganisationUnit organisationUnit = organisationUnitService.getOrganisationUnit( organisationUnitId );
             reportTable.getRelativeUnits().add( organisationUnit );
             reportTable.setOrganisationUnitName( organisationUnit.getName() );
-            
+
             log.info( "Organisation unit: " + organisationUnit.getName() );
         }
 
@@ -352,7 +441,7 @@
 
         reportTable.setI18nFormat( format );
         reportTable.init();
-        
+
         return reportTable;
     }
 
@@ -364,48 +453,60 @@
      */
     private Grid getGrid( ReportTable reportTable )
     {
-        String subtitle = StringUtils.trimToEmpty( reportTable.getOrganisationUnitName() ) + SPACE + StringUtils.trimToEmpty( reportTable.getReportingMonthName() );
-        
+        String subtitle = StringUtils.trimToEmpty( reportTable.getOrganisationUnitName() ) + SPACE
+            + StringUtils.trimToEmpty( reportTable.getReportingMonthName() );
+
         Grid grid = new ListGrid().setTitle( reportTable.getName() ).setSubtitle( subtitle );
-        
+
         final Map<String, Double> map = reportTableManager.getAggregatedValueMap( reportTable );
-        
+
         // ---------------------------------------------------------------------
         // Headers
         // ---------------------------------------------------------------------
 
         for ( String column : reportTable.getIndexColumns() )
         {
-            grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( column ), column, Integer.class.getName(), true, true ) ); // Index columns
+            grid
+                .addHeader( new GridHeader( PRETTY_COLUMNS.get( column ), column, Integer.class.getName(), true, true ) ); // Index
+            // columns
         }
-        
+
         for ( String column : reportTable.getIndexNameColumns() )
         {
-            grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( column ), column, String.class.getName(), false, true ) ); // Index name columns
+            grid
+                .addHeader( new GridHeader( PRETTY_COLUMNS.get( column ), column, String.class.getName(), false, true ) ); // Index
+            // name
+            // columns
         }
 
-        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( REPORTING_MONTH_COLUMN_NAME ), REPORTING_MONTH_COLUMN_NAME, String.class.getName(), true, true ) );
-        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( PARAM_ORGANISATIONUNIT_COLUMN_NAME ), PARAM_ORGANISATIONUNIT_COLUMN_NAME, String.class.getName(), true, true ) );
-        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME ), ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME, String.class.getName(), true, true ) );
-                
+        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( REPORTING_MONTH_COLUMN_NAME ), REPORTING_MONTH_COLUMN_NAME,
+            String.class.getName(), true, true ) );
+        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( PARAM_ORGANISATIONUNIT_COLUMN_NAME ),
+            PARAM_ORGANISATIONUNIT_COLUMN_NAME, String.class.getName(), true, true ) );
+        grid.addHeader( new GridHeader( PRETTY_COLUMNS.get( ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME ),
+            ORGANISATION_UNIT_IS_PARENT_COLUMN_NAME, String.class.getName(), true, true ) );
+
         for ( List<NameableObject> column : reportTable.getColumns() )
         {
-            grid.addHeader( new GridHeader( getPrettyColumnName( column ), getColumnName( column ), Double.class.getName(), false, false ) );
+            grid.addHeader( new GridHeader( getPrettyColumnName( column ), getColumnName( column ), Double.class
+                .getName(), false, false ) );
         }
-        
+
         if ( reportTable.doSubTotals() )
         {
             for ( DataElementCategoryOption categoryOption : reportTable.getCategoryCombo().getCategoryOptions() )
             {
-                grid.addHeader( new GridHeader( categoryOption.getShortName(), columnEncode( categoryOption.getShortName() ), Double.class.getName(), false, false ) );
+                grid.addHeader( new GridHeader( categoryOption.getShortName(), columnEncode( categoryOption
+                    .getShortName() ), Double.class.getName(), false, false ) );
             }
         }
-        
+
         if ( reportTable.doTotal() )
         {
-            grid.addHeader( new GridHeader( TOTAL_COLUMN_PRETTY_NAME, TOTAL_COLUMN_NAME, Double.class.getName(), false, false ) );
+            grid.addHeader( new GridHeader( TOTAL_COLUMN_PRETTY_NAME, TOTAL_COLUMN_NAME, Double.class.getName(), false,
+                false ) );
         }
-        
+
         // ---------------------------------------------------------------------
         // Values
         // ---------------------------------------------------------------------
@@ -413,37 +514,49 @@
         for ( List<NameableObject> row : reportTable.getRows() )
         {
             grid.addRow();
-            
+
             for ( IdentifiableObject object : row )
             {
                 grid.addValue( object.getId() ); // Index columns
             }
-            
+
             for ( NameableObject object : row )
             {
                 grid.addValue( object.getShortName() ); // Index name columns
             }
-            
+
             grid.addValue( reportTable.getReportingMonthName() );
             grid.addValue( reportTable.getOrganisationUnitName() );
             grid.addValue( isCurrentParent( row ) ? YES : NO );
-            
+
             for ( List<NameableObject> column : reportTable.getColumns() )
             {
                 grid.addValue( map.get( getIdentifier( row, column ) ) ); // Values
             }
-            
+
             if ( reportTable.doSubTotals() )
             {
                 for ( DataElementCategoryOption categoryOption : reportTable.getCategoryCombo().getCategoryOptions() )
                 {
-                    grid.addValue( map.get( getIdentifier( row, DataElementCategoryOption.class, categoryOption.getId() ) ) );
+                    grid.addValue( map
+                        .get( getIdentifier( row, DataElementCategoryOption.class, categoryOption.getId() ) ) );
                 }
             }
-            
+
             if ( reportTable.doTotal() )
             {
-                grid.addValue( map.get( getIdentifier( row ) ) ); // Only category option combo is crosstab when total, row identifier will return total
+                grid.addValue( map.get( getIdentifier( row ) ) ); // Only
+                // category
+                // option
+                // combo is
+                // crosstab
+                // when
+                // total,
+                // row
+                // identifier
+                // will
+                // return
+                // total
             }
         }
 
@@ -451,7 +564,7 @@
         {
             addRegressionToGrid( grid, reportTable.getColumns().size() );
         }
-        
+
         // ---------------------------------------------------------------------
         // Sort and limit
         // ---------------------------------------------------------------------
@@ -465,7 +578,7 @@
         {
             grid.limitGrid( reportTable.topLimit() );
         }
-        
+
         return grid;
     }
 
@@ -478,20 +591,21 @@
     private Grid addRegressionToGrid( Grid grid, int numberOfColumns )
     {
         int startColumnIndex = grid.getWidth() - numberOfColumns;
-        
+
         for ( int i = 0; i < numberOfColumns; i++ )
         {
             int columnIndex = i + startColumnIndex;
-            
+
             grid.addRegressionColumn( columnIndex, true );
         }
-        
+
         return grid;
     }
-    
+
     /**
      * Checks whether the given List of IdentifiableObjects contains an object
-     * which is an OrganisationUnit and has the currentParent property set to true.
+     * which is an OrganisationUnit and has the currentParent property set to
+     * true.
      * 
      * @param objects the List of IdentifiableObjects.
      */
@@ -499,12 +613,12 @@
     {
         for ( IdentifiableObject object : objects )
         {
-            if ( object != null && object instanceof OrganisationUnit && ((OrganisationUnit)object).isCurrentParent() )
+            if ( object != null && object instanceof OrganisationUnit && ((OrganisationUnit) object).isCurrentParent() )
             {
                 return true;
             }
         }
-        
+
         return false;
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/META-INF/dhis/beans.xml	2011-06-23 14:44:17 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/META-INF/dhis/beans.xml	2011-07-21 03:29:35 +0000
@@ -22,9 +22,16 @@
     <property name="cacheable" value="true" />
   </bean>
 
+  <bean id="org.hisp.dhis.reporttable.ReportTableGroupStore" class="org.hisp.dhis.hibernate.HibernateGenericStore">
+    <property name="clazz" value="org.hisp.dhis.reporttable.ReportTableGroup" />
+    <property name="sessionFactory" ref="sessionFactory" />
+    <property name="cacheable" value="true" />
+  </bean>
+
   <bean id="org.hisp.dhis.reporttable.ReportTableService" class="org.hisp.dhis.reporttable.impl.DefaultReportTableService">
     <property name="reportTableManager" ref="org.hisp.dhis.reporttable.jdbc.ReportTableManager" />
     <property name="reportTableStore" ref="org.hisp.dhis.reporttable.ReportTableStore" />
+    <property name="reportTableGroupStore" ref="org.hisp.dhis.reporttable.ReportTableGroupStore" />
     <property name="reportService" ref="org.hisp.dhis.report.ReportService" />
     <property name="periodService" ref="org.hisp.dhis.period.PeriodService" />
     <property name="organisationUnitService" ref="org.hisp.dhis.organisationunit.OrganisationUnitService" />
@@ -39,9 +46,16 @@
     <property name="sessionFactory" ref="sessionFactory" />
     <property name="cacheable" value="true" />
   </bean>
+  
+  <bean id="org.hisp.dhis.report.ReportGroupStore" class="org.hisp.dhis.hibernate.HibernateGenericStore">
+    <property name="clazz" value="org.hisp.dhis.report.ReportGroup" />
+    <property name="sessionFactory" ref="sessionFactory" />
+    <property name="cacheable" value="true" />
+  </bean>
 
   <bean id="org.hisp.dhis.report.ReportService" class="org.hisp.dhis.report.impl.DefaultReportService">
     <property name="reportStore" ref="org.hisp.dhis.report.ReportStore" />
+    <property name="reportGroupStore" ref="org.hisp.dhis.report.ReportGroupStore" />
   </bean>
     
   <!-- Chart -->
@@ -51,9 +65,16 @@
     <property name="sessionFactory" ref="sessionFactory" />
     <property name="cacheable" value="true" />
   </bean>
+  
+  <bean id="org.hisp.dhis.chart.ChartGroupStore" class="org.hisp.dhis.chart.hibernate.HibernateChartStore">
+    <property name="clazz" value="org.hisp.dhis.chart.ChartGroup" />
+    <property name="sessionFactory" ref="sessionFactory" />
+    <property name="cacheable" value="true" />
+  </bean>
 
   <bean id="org.hisp.dhis.chart.ChartService" class="org.hisp.dhis.chart.impl.DefaultChartService">
     <property name="chartStore" ref="org.hisp.dhis.chart.ChartStore" />
+    <property name="chartGroupStore" ref="org.hisp.dhis.chart.ChartGroupStore" />
     <property name="periodService" ref="org.hisp.dhis.period.PeriodService" />
     <property name="dataValueService" ref="org.hisp.dhis.datavalue.DataValueService" />
     <property name="minMaxDataElementService" ref="org.hisp.dhis.minmax.MinMaxDataElementService" />

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml	2011-07-07 13:29:56 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml	2011-07-21 03:29:35 +0000
@@ -64,6 +64,12 @@
       <many-to-many column="organisationunitid" class="org.hisp.dhis.organisationunit.OrganisationUnit"
         foreign-key="fk_chart_organisationunits_organisationunitid" />
     </list>
+	
+	<set name="groups" table="chartgroupmembers" inverse="true">
+	  <cache usage="read-write" />
+	  <key column="chartid" />
+	  <many-to-many class="org.hisp.dhis.chart.ChartGroup" column="chartgroupid" />
+	</set>
 
     <component name="relatives">
       <property name="reportingMonth" />

=== added file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/ChartGroup.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/ChartGroup.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/ChartGroup.hbm.xml	2011-07-21 03:29:35 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd";>
+
+<hibernate-mapping>
+  <class name="org.hisp.dhis.chart.ChartGroup" table="chartgroup">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="chartgroupid">
+      <generator class="native" />
+    </id>
+
+    <property name="name">
+      <column name="name" not-null="true" unique="true" length="230" />
+    </property>
+
+    <set name="members" table="chartgroupmembers">
+      <cache usage="read-write" />
+      <key column="chartgroupid" foreign-key="fk_chartgroupmembers_chartgroupid" />
+      <many-to-many class="org.hisp.dhis.chart.Chart" column="chartid" foreign-key="fk_chartgroup_chartid" />
+    </set>
+
+  </class>
+</hibernate-mapping>

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/Report.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/Report.hbm.xml	2011-05-28 21:25:46 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/Report.hbm.xml	2011-07-21 03:29:35 +0000
@@ -20,6 +20,13 @@
 
     <many-to-one name="reportTable" class="org.hisp.dhis.reporttable.ReportTable" column="reporttableid"
       foreign-key="fk_report_reporttableid" />
+	  
+	<set name="groups" table="reportgroupmembers">
+	  <cache usage="read-write" />
+	  <key column="reportid" />
+	  <many-to-many class="org.hisp.dhis.report.ReportGroup" column="reportgroupid" />
+	</set>
+	
 
   </class>
 </hibernate-mapping>
\ No newline at end of file

=== added file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/ReportGroup.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/ReportGroup.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/report/hibernate/ReportGroup.hbm.xml	2011-07-21 03:29:35 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd";>
+
+<hibernate-mapping>
+  <class name="org.hisp.dhis.report.ReportGroup" table="reportgroup">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="reportgroupid">
+      <generator class="native" />
+    </id>
+
+    <property name="name">
+      <column name="name" not-null="true" unique="true" length="230" />
+    </property>
+
+    <set name="members" table="reportgroupmembers">
+      <cache usage="read-write" />
+      <key column="reportgroupid" foreign-key="fk_reportgroupmembers_reportgroupid" />
+      <many-to-many class="org.hisp.dhis.report.Report" column="reportid" foreign-key="fk_reportgroup_reportid" />
+    </set>
+
+  </class>
+</hibernate-mapping>

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml	2011-05-28 21:25:46 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml	2011-07-21 03:29:35 +0000
@@ -56,6 +56,12 @@
       <many-to-many column="organisationunitid" class="org.hisp.dhis.organisationunit.OrganisationUnit" foreign-key="fk_reporttable_organisationunits_organisationunitid" />
     </list>
 
+	<set name="groups" table="reporttablegroupmembers">
+	  <cache usage="read-write" />
+	  <key column="reporttableid" />
+	  <many-to-many class="org.hisp.dhis.reporttable.ReportTableGroup" column="reporttablegroupid" />
+	</set>
+	
     <property name="doIndicators" />
 
     <property name="doPeriods" />

=== added file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTableGroup.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTableGroup.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTableGroup.hbm.xml	2011-07-21 03:29:35 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd";>
+
+<hibernate-mapping>
+  <class name="org.hisp.dhis.reporttable.ReportTableGroup" table="reporttablegroup">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="reporttablegroupid">
+      <generator class="native" />
+    </id>
+
+    <property name="name">
+      <column name="name" not-null="true" unique="true" length="230" />
+    </property>
+
+    <set name="members" table="reporttablegroupmembers">
+      <cache usage="read-write" />
+      <key column="reporttablegroupid" foreign-key="fk_reporttablegroupmembers_reporttablegroupid" />
+      <many-to-many class="org.hisp.dhis.reporttable.ReportTable" column="reporttableid" foreign-key="fk_reporttablegroup_reporttableid" />
+    </set>
+
+  </class>
+</hibernate-mapping>

=== modified file 'dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml'
--- dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml	2011-07-14 10:31:16 +0000
+++ dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml	2011-07-21 03:29:35 +0000
@@ -110,12 +110,21 @@
   
   <cache name="org.hisp.dhis.chart.Chart"
     maxElementsInMemory="100"/>
+	
+  <cache name="org.hisp.dhis.chart.ChartGroup"
+    maxElementsInMemory="100"/>
   
   <cache name="org.hisp.dhis.reporttable.ReportTable"
     maxElementsInMemory="100"/>
   
+  <cache name="org.hisp.dhis.reporttable.ReportTableGroup"
+    maxElementsInMemory="100"/>
+  
   <cache name="org.hisp.dhis.report.Report"
     maxElementsInMemory="100"/>
+  
+  <cache name="org.hisp.dhis.report.ReportGroup"
+    maxElementsInMemory="100"/>
     
   <cache name="org.hisp.dhis.document.Document"
     maxElementsInMemory="100"/>
@@ -200,6 +209,12 @@
   <cache name="org.hisp.dhis.organisationunit.OrganisationUnitGroupSet.organisationUnitGroups"
     maxElementsInMemory="400"/>
   
+  <cache name="org.hisp.dhis.report.Report.groups"
+    maxElementsInMemory="400"/>
+  
+  <cache name="org.hisp.dhis.report.ReportGroup.members"
+    maxElementsInMemory="400"/>
+  
   <cache name="org.hisp.dhis.reporttable.ReportTable.dataElements"
     maxElementsInMemory="1500"/>
   
@@ -215,6 +230,12 @@
   <cache name="org.hisp.dhis.reporttable.ReportTable.units"
     maxElementsInMemory="2000"/>
     
+  <cache name="org.hisp.dhis.reporttable.ReportTable.groups"
+    maxElementsInMemory="500"/>
+    
+  <cache name="org.hisp.dhis.reporttable.ReportTableGroup.members"
+    maxElementsInMemory="500"/>
+    
   <cache name="org.hisp.dhis.chart.Chart.dataElements"
     maxElementsInMemory="1500"/>
     
@@ -227,6 +248,12 @@
   <cache name="org.hisp.dhis.chart.Chart.organisationUnits"
     maxElementsInMemory="2000"/>
     
+  <cache name="org.hisp.dhis.chart.Chart.groups"
+    maxElementsInMemory="500"/>
+    
+  <cache name="org.hisp.dhis.chart.ChartGroup.members"
+    maxElementsInMemory="500"/>
+    
   <cache name="org.hisp.dhis.user.User.organisationUnits"
     maxElementsInMemory="20000"/>
     

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/UUIdUtils.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/UUIdUtils.java	2010-04-12 21:23:33 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/UUIdUtils.java	2011-07-21 03:29:35 +0000
@@ -1,7 +1,7 @@
 package org.hisp.dhis.system.util;
 
 /*
- * Copyright (c) 2004-2010, University of Oslo
+ * Copyright (c) 2004-2011, University of Oslo
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without