    View Javadoc
    1   /*
    2    * Copyright (c) 2003
    3    * Information Desire GmbH
    4    * All rights reserved.
    5    */
    6   package com.infodesire.infobit.external.impl;
    7
    8   import com.infodesire.infobit.InfobitManager;
    9   import com.infodesire.infobit.InfobitException;
    10  import com.infodesire.infobit.InfobitSecurityException;
    11
    12  import com.infodesire.infobit.data.Acl;
    13  import com.infodesire.infobit.data.Infobit;
    14
    15  import com.infodesire.infobit.external.ImportMessage;
    16  import com.infodesire.infobit.external.InfobitImporter;
    17
    18  import org.apache.commons.logging.Log;
    19  import org.apache.commons.logging.LogFactory;
    20
    21  import java.io.Reader;
    22
    23  import java.util.ArrayList;
    24  import java.util.Comparator;
    25  import java.util.HashMap;
    26  import java.util.HashSet;
    27  import java.util.Iterator;
    28  import java.util.List;
    29  import java.util.Map;
    30  import java.util.Set;
    31  import java.util.SortedSet;
    32  import java.util.TreeSet;
    33
    34  /***
    35   * This class provides a straight-forward implementation to import infobits from
    36   * their XML representation, as defined by {@link InfobitImporter}.
    37   *
    38   * @author    peter2
    39   * @created   1. September 2003
    40   * @version   $Revision: 1.1 $
    41   */
    42  public class ImporterImpl
    43       implements InfobitImporter, InfobitDictionary, ImportDiagnostics {
    44
    45      private final static Log _log = LogFactory.getLog(ImporterImpl.class);
    46
    47      /***
    48       * The ACL used to create infobits and content.
    49       */
    50      private Acl     _acl;
    51
    52      /***
    53       * The collaborating manager
    54       */
    55      private InfobitManager _manager;
    56
    57      /***
    58       * System ID of the XML document to import
    59       */
    60      private String  _path;
    61
    62      /***
    63       * List of diagnostics messages applicable to the currently processed {@link
    64       * importInfobits import operation}. List of {@link ImportMessage}.
    65       */
    66      private List    _diagnostics;
    67
    68      /***
    69       * List of all imported infobits after completion, in order of import. List
    70       * of {@link Infobit}.
    71       */
    72      private List    _infobits = new ArrayList();
    73
    74      /***
    75       * Lexicon of all infobits involved in the import. Maps <code>String</code>
    76       * infobit names to {@link InfobitNode}s representing infobits.
    77       */
    78      private Map     _infobitRegister = new HashMap();
    79
    80      /***
    81       * Collects all buffered infobits and version nodes, ordered by definition
    82       * in the XML import document. Set of {@link BufferNode}.
    83       */
    84      private List    _bufferNodes;
    85
    86      /***
    87       * Next timestamp value used for {@link #colorGraph depth-first search}
    88       */
    89      private int     _nextTime = 1;
    90
    91      /***
    92       * Buffer nodes in linear dependency order, with the least dependent nodes
    93       * first. Set of {@link BufferNode}.
    94       */
    95      private SortedSet _orderedNodes =
    96          new TreeSet(
    97          new Comparator() {
    98              public int compare(Object a, Object b) {
    99                  BufferNode x = (BufferNode) a;
    100                 BufferNode y = (BufferNode) b;
    101
    102                 return x.getCompleteTime() - y.getCompleteTime();
    103             }
    104
    105
    106             public boolean equals(Object obj) {
    107                 return false;
    108             }
    109         });
    110
    111     /***
    112      * For each dependency cycle, this collection contains an edge closing the
    113      * cycle (backward edge). Set of {@link BufferEdges}.
    114      */
    115     private Set     _backwardEdges = new HashSet();
    116
    117     /***
    118      * Maps all processed infobit nodes to their resolved infobits. Map of
    119      * {@link InfobitNode} to {@link Infobit}.
    120      */
    121     private Map     _resolvedInfobits = new HashMap();
    122
    123
    124     /***
    125      * Creates an instance ready to set the manager and to {@link #init
    126      * initialize}.
    127      */
    128     public ImporterImpl() {
    129         //  STUB
    130     }
    131
    132
    133     /***
    134      * Gets the manager to work with.
    135      *
    136      * @return   The manager to access infobits
    137      */
    138     public InfobitManager getManager() {
    139         return _manager;
    140     }
    141
    142
    143     /***
    144      * Defines the manager to work with.
    145      *
    146      * @param manager  The manager to access infobits
    147      */
    148     public void setManager(InfobitManager manager) {
    149         _manager = manager;
    150     }
    151
    152
    153     /***
    154      * Defines the ACL used to create imported infobits and content
    155      *
    156      * @param acl  The new Acl value
    157      */
    158     public void setAcl(Acl acl) {
    159         _acl = acl;
    160     }
    161
    162
    163     /***
    164      * Recovers infobits from an XML representation as created by {@link
    165      * InfobitExporter#export InfobitExporter.export}, as specified for {@link
    166      * InfobitImporter}. <p>
    167      *
    168      * <em<BUGS</em> : This implementation returns infobits not imported but
    169      * referenced from imported ones.
    170      *
    171      * @param input                         Source from which to obtain the XML
    172      *      document representing the infobits to import
    173      * @param path                          System location of <code>input</code>
    174      *      , used in diagnostic messages
    175      * @param diagnostics                   A list to be filled with {@link
    176      *      ImportMessage} instances providing diagnostic messages about errors
    177      *      encountered during the import.
    178      * @return                              A collection of the imported
    179      *      infobits.
    180      * @exception InfobitException          Generic processing error
    181      * @exception InfobitSecurityException  Description of Exception
    182      */
    183     public List importInfobits(Reader input, String path, List diagnostics)
    184          throws InfobitException, InfobitSecurityException {
    185
    186         _path = path;
    187         _diagnostics = diagnostics;
    188         _diagnostics.clear();
    189         _infobits.clear();
    190         _infobitRegister.clear();
    191
    192         buildBuffer(input);
    193         orderNodes();
    194         checkNodes();
    195
    196         if (_diagnostics.size() == 0) {
    197             persistNodes();
    198             extractInfobits();
    199         }
    200
    201         return _infobits;
    202     }
    203
    204
    205     /***
    206      * Prepares the instance to be used. No action for this implementation.
    207      *
    208      * @exception InfobitException  Description of Exception
    209      */
    210     public void init() throws InfobitException {
    211     }
    212
    213
    214     ////////////////////////////////////////////////////////////////////////
    215     // InfobitDictionary Implementation
    216     ////////////////////////////////////////////////////////////////////////
    217
    218     /***
    219      * DOCUMENT METHOD
    220      *
    221      * @param node  Description of Parameter
    222      */
    223     public void register(InfobitNode node) {
    224         _infobitRegister.put(node.getName(), node);
    225         node.setDangling(false);
    226     }
    227
    228
    229     /***
    230      * DOCUMENT METHOD
    231      *
    232      * @param name  Description of Parameter
    233      * @return      Description of the Returned Value
    234      */
    235     public InfobitNode lookup(String name) {
    236         InfobitNode node = (InfobitNode) _infobitRegister.get(name);
    237
    238         if (node == null) {
    239             node = new InfobitNode();
    240             node.setName(name);
    241             node.setDangling(true);
    242             _infobitRegister.put(name, node);
    243         }
    244
    245         return node;
    246     }
    247
    248
    249     ////////////////////////////////////////////////////////////////////////
    250     // ImportDiagnostics Implementation
    251     ////////////////////////////////////////////////////////////////////////
    252
    253     /***
    254      * DOCUMENT METHOD
    255      *
    256      * @param message  Description of Parameter
    257      */
    258     public void record(ImportMessage message) {
    259         _diagnostics.add(message);
    260         _log.trace("Import error: " + message.getMessage());
    261     }
    262
    263
    264     ////////////////////////////////////////////////////////////////////////
    265     // Implementation private
    266     ////////////////////////////////////////////////////////////////////////
    267
    268     /***
    269      * Converts an XML document representing exported infobits into the buffer
    270      * graph. The graph is not coloured, and references are not resolved. Sets
    271      * {@link #_bufferNodes}.
    272      *
    273      * @param input                         Description of Parameter
    274      * @exception InfobitException          Description of Exception
    275      * @exception InfobitSecurityException  Description of Exception
    276      */
    277     private void buildBuffer(Reader input)
    278          throws InfobitException, InfobitSecurityException {
    279
    280         InfobitParser parser = new InfobitParser(this, this);
    281         parser.setSystemID(_path);
    282         parser.buildTree(input);
    283         _bufferNodes = parser.getBufferNodes();
    284     }
    285
    286
    287     /***
    288      * Checks the dependency graph fro acyclicity and creates a linear order.
    289      */
    290     private void orderNodes() {
    291         for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
    292             BufferNode node = (BufferNode) i.next();
    293
    294             if (node.getColor() == BufferNode.WHITE) {
    295                 colorGraph(node);
    296             }
    297         }
    298
    299         reportCycles();
    300     }
    301
    302
    303     /***
    304      * Checks the linearly ordered items to be imported on consistency.
    305      *
    306      * @exception InfobitException  Description of Exception
    307      */
    308     private void checkNodes() throws InfobitException {
    309         for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
    310             BufferNode node = (BufferNode) i.next();
    311
    312             if (node instanceof InfobitNode) {
    313                 check((InfobitNode) node);
    314             }
    315             else if (node instanceof ActualVersionNode) {
    316                 check((ActualVersionNode) node);
    317             }
    318         }
    319     }
    320
    321
    322     /***
    323      * Successively resolves buffer dependencies and persists the items to be
    324      * imported. Stores imported infobits to {@link _resolvedInfobits}.
    325      *
    326      * @exception InfobitException          Description of Exception
    327      * @exception InfobitSecurityException  Description of Exception
    328      */
    329     private void persistNodes()
    330          throws InfobitException, InfobitSecurityException {
    331
    332         _resolvedInfobits.clear();
    333         Map persistedNodes = new HashMap();
    334
    335         for (Iterator i = _orderedNodes.iterator(); i.hasNext(); ) {
    336             BufferNode node = (BufferNode) i.next();
    337
    338             node.setManager(_manager);
    339             node.setAcl(_acl);
    340
    341             for (Iterator j = node.getDependencies().iterator();
    342                 j.hasNext(); ) {
    343
    344                 BufferNode dep = (BufferNode) j.next();
    345                 Object p = persistedNodes.get(dep);
    346
    347                 //  NOTE: due to the linear order of dependencies,
    348                 //  p != null is an invariant
    349                 if (p == null) {
    350                     String m = "Failed assertion: unresolved dependency of " +
    351                         node.getClass().getName() +
    352                         " to " + dep.getClass().getName();
    353                     _log.fatal(m);
    354                     throw new RuntimeException(m);
    355                 }
    356
    357                 node.resolveDependency(dep, p);
    358             }
    359
    360             Object persisted = node.persistBuffer();
    361             persistedNodes.put(node, persisted);
    362
    363             if (node instanceof InfobitNode) {
    364                 InfobitNode n = (InfobitNode) node;
    365
    366                 //  TEST
    367                 //  if (!n.isPredefined()) {
    368                 if (true || !n.isPredefined()) {
    369                     _resolvedInfobits.put(node, persisted);
    370                 }
    371             }
    372         }
    373     }
    374
    375
    376     /***
    377      * Sets up the final ordered list of imported infobits.
    378      */
    379     private void extractInfobits() {
    380         _infobits.clear();
    381
    382         for (Iterator i = _bufferNodes.iterator(); i.hasNext(); ) {
    383             BufferNode bn = (BufferNode) i.next();
    384             Infobit ib = (Infobit) _resolvedInfobits.get(bn);
    385
    386             if (ib != null) {
    387                 _infobits.add(ib);
    388             }
    389         }
    390     }
    391
    392
    393     /***
    394      * Performs depth-first search on the nodes reachable from the specfied
    395      * node. Colors all reached nodes and edges. Stores nodes to {@link
    396      * _orderedNodes}.
    397      *
    398      * @param node  Description of Parameter
    399      */
    400     private void colorGraph(BufferNode node) {
    401         node.setColor(BufferNode.GRAY);
    402         node.setDiscoverTime(_nextTime++);
    403
    404         for (Iterator i = node.getEdges().iterator(); i.hasNext(); ) {
    405             BufferEdge edge = (BufferEdge) i.next();
    406             BufferNode end = edge.getDependency();
    407
    408             switch (end.getColor()) {
    409                 case BufferNode.WHITE:
    410                     colorGraph(end);
    411                     // edge.setKind(BufferEdge.TREE);
    412                     break;
    413                 case BufferNode.GRAY:
    414                     //  edge.setKind(BufferEdge.BACKWARD);
    415                     _backwardEdges.add(edge);
    416                     break;
    417                 case BufferNode.BLACK:
    418                     //  edge.setKind(BufferEdge.FORWARD_OR_CROSS);
    419                     break;
    420             }
    421         }
    422
    423         node.setColor(BufferNode.BLACK);
    424         node.setCompleteTime(_nextTime++);
    425         _orderedNodes.add(node);
    426     }
    427
    428
    429     /***
    430      * Creates error messages on cyclic dependencies from {@link
    431      * _backwardNodes}.
    432      */
    433     private void reportCycles() {
    434         for (Iterator i = _backwardEdges.iterator(); i.hasNext(); ) {
    435             BufferEdge edge = (BufferEdge) i.next();
    436             BufferNode node = edge.getDependency();
    437             error(node, "This node infers a cyclic dependency");
    438         }
    439
    440         //  Currently, no cycles are possible
    441         //  if (_backwardEdges.size() > 0) {
    442         //  	_log.fatal("Failed assertion: cyclic dependency");
    443         //  	throw new RuntimeException("Failed assertion: cyclic dependency");
    444         //  }
    445     }
    446
    447
    448     /***
    449      * Checks consistency of an infobit to create. Adds error messges.
    450      *
    451      * @param node                  Description of Parameter
    452      * @exception InfobitException  Description of Exception
    453      */
    454     private void check(InfobitNode node)
    455          throws InfobitException {
    456
    457         String name = node.getName();
    458         boolean predefined = _manager.hasInfobit(name);
    459         node.setPredefined(predefined);
    460
    461         if (node.isDangling()) {
    462             node.setDangling(predefined);
    463         }
    464         else if (predefined) {
    465             error(node,
    466                 "The node '" + name + "' to import already exists" +
    467                 " in the system.");
    468         }
    469
    470         if (node.isDangling()) {
    471             error(node,
    472                 "The referenced Node '" + name + "' is neither" +
    473                 " imported nor pre-existent in the system");
    474
    475             for (Iterator i = node.getDependents().iterator(); i.hasNext(); ) {
    476                 BufferEdge edge = (BufferEdge) i.next();
    477                 error(node, "Node '" + name + "' referenced from here");
    478             }
    479         }
    480
    481     }
    482
    483
    484     /***
    485      * Checks the assignement of an actual version for consistency. Emits error
    486      * messages if appropriate.
    487      *
    488      * @param node  Description of Parameter
    489      */
    490     private void check(ActualVersionNode node) {
    491         if (node.getActualVersionNode() == null) {
    492             error(node,
    493                 "Node '" +
    494                 node.getInfobitNode().getName() + "' has" +
    495                 "an undefined version set as its actual version.");
    496         }
    497     }
    498
    499
    500     /***
    501      * Records an error condition for the specified buffer node. Additionally,
    502      * logs the error.
    503      *
    504      * @param node  The node to which <code>message</code> applies
    505      * @param msg   Description of Parameter
    506      */
    507     private void error(BufferNode node, String msg) {
    508         String path = node.locateSystemID();
    509         int line = node.locateLine();
    510
    511         StringBuffer b = new StringBuffer();
    512         if (path != null) {
    513             b.append(path);
    514             b.append(":");
    515         }
    516
    517         if (b.length() > 0 && line > 0) {
    518             b.append(":");
    519             b.append(line);
    520         }
    521
    522         b.append(" Error in import: ");
    523         b.append(msg);
    524
    525         _log.error(b.toString());
    526         _diagnostics.add(new ImportMessage(path, line, 0, msg));
    527     }
    528
    529 }
    530

This page was automatically generated by Maven