/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.sparql.core;

import java.util.HashMap ;
import java.util.Iterator ;
import java.util.Map ;

import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
import org.apache.jena.query.ReadWrite ;
import org.apache.jena.sparql.ARQException ;
import org.apache.jena.sparql.core.DatasetGraphFactory.GraphMaker ;

/** Implementation of a DatasetGraph as an extensible set of graphs.
 *  <p>
 *  Graphs are held by reference. Care is needed when manipulating their contents
 *  especially if they are also in another {@code DatasetGraph}.
 *  <p> 
 *  See {@link DatasetGraphMap} for an implementation that copies graphs
 *  and so providing better isolation.
 *  <p>
 *  This class is best used for creating views  
 *  
 *  @see DatasetGraphMap
 */
public class DatasetGraphMapLink extends DatasetGraphCollection
{
    
    private final GraphMaker graphMaker ;
    private final Map<Node, Graph> graphs = new HashMap<>() ;

    private Graph defaultGraph ;

    /**
     * Create a new {@code DatasetGraph} that copies the dataset structure of default
     * graph and named graph and links to the graphs of the original {@code DatasetGraph}.
     * Any new graphs needed are separate from the original dataset and created in-memory. 
     */
    public static DatasetGraph cloneStructure(DatasetGraph dsg) {
        return new DatasetGraphMapLink(dsg);
    }
    
    /**
     * Create a new {@code DatasetGraph} that copies the dataset structure of default
     * graph and named graph and links to the graphs of the original {@code DatasetGraph}
     * Any new graphs needed are separate from the original dataset and created according
     * to the {@link GraphMaker}.
     */
    public static DatasetGraph cloneStructure(DatasetGraph dsg, GraphMaker graphMaker) {
        return new DatasetGraphMapLink(dsg, graphMaker);
    }

    /** Create a new DatasetGraph that initially shares the graphs of the
     * given DatasetGraph.  Adding/removing graphs will only affect this
     * object, not the argument DatasetGraph but changes to shared
     * graphs are seen by both objects.
     */
    private DatasetGraphMapLink(DatasetGraph dsg, GraphMaker graphMaker) {
        this.graphMaker = graphMaker ; 
        this.defaultGraph = dsg.getDefaultGraph() ;
        for ( Iterator<Node> names = dsg.listGraphNodes() ; names.hasNext() ; ) {
            Node gn = names.next() ;
            addGraph(gn, dsg.getGraph(gn)) ;
        }
    }

    /** 
     *  A {@code DatasetGraph} with graphs for default and named graphs as given
     *  but new graphs are created in memory.
     */
    private DatasetGraphMapLink(DatasetGraph dsg) {
        this(dsg, DatasetGraphFactory.memGraphMaker) ;
    }

    private DatasetGraphMapLink(Graph dftGraph, GraphMaker graphMaker) {
        this.graphMaker = graphMaker;
        this.defaultGraph = dftGraph ;
    }
    
//    /** A {@code DatasetGraph} with in-memory graphs for default and named graphs as needed */ 
//    private DatasetGraphMapLink() {
//        this(DatasetGraphFactory.memGraphMaker) ; 
//    }
    
    /**
     * A {@code DatasetGraph} with graph from the gve {@link GraphMaker} for default and
     * named graphs as needed. This is the constructor used for
     * DatasetFactory.createGeneral.
     */
    /*package*/ DatasetGraphMapLink(GraphMaker graphMaker) {
        this(graphMaker.create(), graphMaker) ;
    }

    /** A {@code DatasetGraph} that uses the given graph for the default graph
     *  and create in-memory graphs for named graphs as needed
     */
    public DatasetGraphMapLink(Graph dftGraph) {
        this.defaultGraph = dftGraph ;
        this.graphMaker = DatasetGraphFactory.graphMakerNull ;
    }

    // ----
    private final Transactional txn                     = TransactionalLock.createMRSW() ;
    @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
    @Override public void commit()                      { txn.commit() ; }
    @Override public void abort()                       { txn.abort() ; }
    @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
    @Override public void end()                         { txn.end(); }
    @Override public boolean supportsTransactions()     { return true ; }
    @Override public boolean supportsTransactionAbort() { return false ; }
    // ----

    @Override
    public boolean containsGraph(Node graphNode) {
        return graphs.containsKey(graphNode);
    }

    @Override
    public Graph getDefaultGraph() {
        return defaultGraph;
    }

    @Override
    public Graph getGraph(Node graphNode) {
        Graph g = graphs.get(graphNode);
        if ( g == null ) {
            g = getGraphCreate();
            if ( g != null )
                addGraph(graphNode, g);
        }
        return g;
    }

    /** Called from getGraph when a nonexistent graph is asked for.
     * Return null for "nothing created as a graph"
     */
    protected Graph getGraphCreate() { 
        Graph g = graphMaker.create() ;
        if ( g == null )
            throw new ARQException("Can't make new graphs") ;
        return g ;
    }

    @Override
    public void addGraph(Node graphName, Graph graph) {
        graphs.put(graphName, graph);
    }

    @Override
    public void removeGraph(Node graphName) {
        graphs.remove(graphName);
    }

    @Override
    public void setDefaultGraph(Graph g) {
        defaultGraph = g;
    }

    @Override
    public Iterator<Node> listGraphNodes() {
        return graphs.keySet().iterator();
    }

    @Override
    public long size() {
        return graphs.size();
    }

    @Override
    public void close() {
        defaultGraph.close();
        for ( Graph graph : graphs.values() )
            graph.close();
        super.close();
    }
}

@@ -1035,53 +1035,8 @@
 e ;%0A
-import org.apache.jena.sparql.ARQException ;%0A
 impo
@@ -1099,16 +1099,16 @@
 Maker ;%0A
+
 %0A/** Imp
@@ -3458,36 +3458,33 @@
 setGraphFactory.
-memG
+g
 raphMaker) ;%0A   
@@ -3468,32 +3468,35 @@
 ctory.graphMaker
+Mem
 ) ;%0A    %7D%0A%0A    p
@@ -4473,12 +4473,11 @@
 aker
-Null
+Mem
  ;%0A 
@@ -5777,33 +5777,30 @@
  %7B %0A        
-Graph g =
+return
  graphMaker.
@@ -5814,114 +5814,8 @@
 ) ;%0A
-        if ( g == null )%0A            throw new ARQException(%22Can't make new graphs%22) ;%0A        return g ;%0A
     

Allow null after create. Use mem graph maker as default.