!/usr/bin/perl -w
 4AIDCLW - XML::Merge.pm created by Pip Stuart <Pip@CPAN.Org>
   to intelligently merge XML documents as parsed XML::XPath objects.

 Plan:
   if    same-named root nodes,
     merge straight
   elsif root of 2nd exists in 1st,
     merge at first match
   else
     append 2nd root as new last child of 1st root

     XML::Merge new(filename => 'fnam'[, <other options> ])
       inherits XML::Tidy which inherits XML::XPath.
       Merge creates an object with a merge() member which creates another
       XPath object && combines the result back into the main object.
     optn:
       merge below specified context
       id attributes: 'id', 'name', && 'handle' (default)
       join comments of same context (leave separate default)
       source-file-stamp merged comments
              time-stamp merged comments
                pt-stamp merged comments
     conflict rules:
       main    wins (default)
       last-in wins (aka. clobber)
       newer modification date wins
       warn (croak conflict)
       test (don't merge anything, just return true if no conflicts)
     members:
       merge() (can accept tmp override optz)
       unmerge()

   option to rename some XPath to something else so like simple example
     is taking merge-file's root node element && pretending it is
     named the same as the main-file's root node element so that the
     two can merge in place even though their root node elements had
     different names.  This would clobber the name of the merge-file
     element with the main-file one but it would be a useful option.


NAME

XML::Merge - flexibly merge XML documents

VERSION

This documentation refers to version 1.0.4CAL5IS of 
XML::Merge, which was released on Fri Dec 10 21:05:18:28 2004.

SYNOPSIS

  use XML::Merge;

  # create new    XML::Merge object from         MainFile.xml
  my $merge_obj = XML::Merge->new('filename' => 'MainFile.xml');

  # Merge File2Add.xml              into         MainFile.xml
     $merge_obj->merge(           'filename' => 'File2Add.xml');

  # Tidy up the indenting that resulted from the merge
     $merge_obj->tidy();

  # Write out changes back            to         MainFile.xml
     $merge_obj->write();

DESCRIPTION

This module inherits from L<XML::Tidy> which in turn inherits from
L<XML::XPath>.  This ensures that Merge objects' indenting can be
tidied up after any merge operation since such modification usually
spells the ruination of indentation.  Polymorphism allows Merge
objects to be utilized as normal XML::XPath objects as well.

The merging behavior is setup to combine separate XML documents
according to certain rules && configurable options.  If both
documents have root nodes which are elements of the same name, the
documents are merged directly.  Otherwise, one is merged as a child
of the other.  An optional XPath location can be specified as the
place to perform the merge.  If no location is specified, the merge
is attempted at the first matching element or is appended as the new
last child of the other root if no match is found.

2DO

- mk namespaces && attz stay in order after merge()

- mk txt apnd merg optn

- handle comment joins && stamping && options

- support modification-time _cres

- add _ignr ignore list of merg xplc's to not merge (pre-prune())

- support _idea options where several attz together are single id

-     What else does Merge need?

USAGE

new()

This is the standard Merge object constructor.  It can take the
same parameters as an L<XML::XPath> object constructor to initialize
the primary XML document object (the object which subsequent XML
documents will be merged into).  These parameters can be any one of:

  'filename' => 'SomeFile.xml'
  'xml'      => $variable_which_holds_a_bunch_of_XML_data
  'ioref'    => $file_InputOutput_reference
  'context'  => $existing_node_at_specified_context_to_become_new_obj

Merge's new() can also accept merge-option parameters to
override the default merge behavior.  These include:

  'conflict_resolution_method' => 'main', # main  file wins
  'conflict_resolution_method' => 'merg', # merge file wins
                   # 'last-in_wins' is an alias for 'merg'
  'conflict_resolution_method' => 'warn', # croak conflicts
  'conflict_resolution_method' => 'test', # just test, 0 if conflict
  # this option is not implemented yet
  'comment_join_method' => 'none',

merge()

The merge() member function can accept the same L<XML::XPath>
constructor options as new() but this time they are for the
temporary file which will be merged into the main object.
Merge-options from new() can also be specified && they will only
impact one particular invokation of merge().  The specified document
will be merged into the primary XML document object according to
the following default merge rules:

  0. If both documents share the same root element name, they are
       merged directly.
  1. If they don't share root elements but the temporary merge file's
       root element is found anywhere within the main file, the merge
       occurs at the match.
  2. If no root element match is found, the merge document becomes the
       new last child of the main file's root element.
  3. Whenever a deeper level is found with an element of the same name
       in both documents && either it does not contain any
       distinguishing attributes or it has attributes which are
       recognized as 'identifier' (id) attributes (by default, for any
       element, these are attributes named: 'id', 'name', && 'handle'),
       a corresponding element is searched for to match && merge with.
  4. Any remaining (non-id) nodes are merged in document order.
  5. When a conflict arises as non-id attributes or other nodes merge,
       the specified conflict_resolution_method merge-option is
       applied (which by default has the main file data persist at the
       expense of the merging file data).

Some of the above rules can be overridden first by the object's
merge-options && second by the particular method call's merge-options.
Thus, if the default merge-option for conflict resolution is to
have the main object win && you use the following constructor:

  my $merge_obj = XML::Merge->new(
    'filename'                   => 'MainFile.xml',
    'conflict_resolution_method' => 'last-in_wins');

... then any $merge_obj->merge() call would override the
default merge behavior by letting the document being merged have
priority over the main object's document.  However, you could
supply additional merge-options in the parameter list of your
specific merge() call like:

  $merge_obj->merge(
    'filename'                   => 'File2Add.xml',
    'conflict_resolution_method' => 'warn');

... then the latest option would override the already overridden.

The 'test' conflict_resolution_method merge-option does not modify the
object at all.  It solely returns true if no conflict is encountered.
It should be used like:

  foreach(@files) {
    if($merge_obj->merge('cres' => 'test', $_)) {
      $merge_obj->merge($_); # only do it if there's no conflicts
    } else {
      croak("Yipes! Conflict with file:$_!\n");
    }
  }

merge() can also accept another XML::Merge object as a parameter
for what to be merged with the main object instead of a filename.
An example of this is:

  $merge_obj->merge($another_merge_obj);

Along with the merge options that can be specified in the object
constructor, merge() also accepts the following options to specify
where to perform the merge relative to:

  'merge_destination_path' => $main_obj_xpath,
  'merge_source_path'      => $merging_obj_xpath,

unmerge()

The unmerge() member function is a shorthand for calling both write()
&& prune() on a certain XPath location which should be written out
to a disk file before being removed from the Merge object.

This unmerge() process could be the opposite of merge() if no original
elements or attributes overlapped && combined but if combining did
happen, this would remove original sections of your primary XML
document's data from your Merge object so please use this carefully.
It is meant to help separate a giant object (probably the result of
myriad merge() calls) back into separate useful well-formed XML
documents on disk.

unmerge() takes a filename && an xpath_location parameter.

Accessors

get_object_to_merge()

Returns the object which was last merged into the main object.

set_object_to_merge()

Assigns the object which was last merged into the main object.

get_conflict_resolution_method()

Returns the underlying merge-option conflict_resolution_method.

set_conflict_resolution_method()

A new value can be provided as a parameter to be assigned
as the XML::Merge object's merge-option.

get_comment_join_method()

Returns the underlying merge-option comment_join_method.

set_comment_join_method()

A new value can be provided as a parameter to be assigned
as the XML::Merge object's merge-option.

get_id_xpath_list()

Returns the underlying id_xpath_list.  This is normally just a list
of attributes (eg. '@id', '@name', '@handle') which are unique
identifiers for any XML element.  When these attribute names are
encountered during a merge(), another element with the same name &&
attribute value are matched for further merging && conflict resolution.

set_id_xpath_list()

A new list can assigned to the XML::Merge object's id_xpath_list.

CHANGES

Revision history for Perl extension XML::Merge:

- 1.0.4CAL5IS  Fri Dec 10 21:05:18:28 2004

* fixed buggy _recmerge

- 1.0.4CAEU0I  Fri Dec 10 14:30:00:18 2004

* made accessors for _id_xpath_list

* made _id_xpath_list take XPath locations instead of elem names (old _idea)

* made test _cres (at Marc's request)

* made warn _cres croak

* made Merge inherit from Tidy (which inherits from XPath)

* separated reload(), strip(), tidy(), prune(), && write() into own
    XML::Tidy module

- 1.0.4C2Nf0R  Thu Dec  2 23:41:00:27 2004

* updated license && prep'd for release

- 1.0.4C2BcI2  Thu Dec  2 11:38:18:02 2004

* updated reload(), strip(), && tidy() to verify _xpob exists

- 1.0.4C1JHOl  Wed Dec  1 19:17:24:47 2004

* commented out override stuff since it's probably bad form && dumps crap
    warnings all over tests && causes them to fail... so I guess just
    uncomment that stuff if you care to preserve PI's && escapes

- 1.0.4C1J7gt  Wed Dec  1 19:07:42:55 2004

* made merge() accept merge_source_xpath && merge_destination_xpath params

* made merge() accept other Merge objects

* made reload() not clobber basic escapes (by overriding Text toString())

* made tidy() not kill processing-instructions (by overriding node_test())

* made tidy() not kill comments

- 1.0.4BOHGjm  Wed Nov 24 17:16:45:48 2004

* fixed merge() same elems with diff ids bug

- 1.0.4BNBCZL  Tue Nov 23 11:12:35:21 2004

* rewrote both merge() && _recmerge() _cres stuff since it was
    buggy before... so hopefully consistently good now

- 1.0.4BMJCPm  Mon Nov 22 19:12:25:48 2004

* fixed merge() for empty elem matching && _cres on text kids

- 1.0.4BMGTLF  Mon Nov 22 16:29:21:15 2004

* separated reload() from strip() so that prune() can call it too

- 1.0.4BM0B3x  Mon Nov 22 00:11:03:59 2004

* fixed tidy() empty elem bug && implemented prune() && unmerge()

- 1.0.4BJAZpM  Fri Nov 19 10:35:51:22 2004

* fixing e() ABSTRACT gen bug

- 1.0.4BJAMR6  Fri Nov 19 10:22:27:06 2004

* fleshed out pod && members

- 1.0.4AIDqmR  Mon Oct 18 13:52:48:27 2004

* original version

INSTALL

From your command shell, please run:

    `perl -MCPAN -e "install XML::Merge"`

or uncompress the package && run the standard:

    `perl Makefile.PL; make; make test; make install`

FILES

XML::Merge requires:

L<Carp>                to allow errors to croak() from calling sub

L<XML::XPath>          to use XPath statements to query && update XML

LICENSE

Most source code should be Free!
  Code I have lawful authority over is && shall be!
Copyright: (c) 2004, Pip Stuart.
Copyleft : This software is licensed under the GNU General Public
  License (version 2), && as such comes with NO WARRANTY.  Please
  consult the Free Software Foundation (http://FSF.Org) for
  important information about your freedom.

AUTHOR

Pip Stuart <Pip@CPAN.Org>

