module ActiveFedora::DatastreamCollections

def add_named_datastream(name,opts={})

:dsid or :dsId => Optional, and used to update an existing datastream with dsid supplied. Will throw an error if dsid does not exist and does not match prefix pattern for datastream name
:dsLocation => holds uri location of datastream. Required only if :controlGroup is type 'E' or 'R'.
:content_type => required if the file does not respond to 'content_type' and should match :mimeType in has_datastream definition if defined
:controlGroup => defaults to 'M' for managed, can also be 'E' external and 'R' for redirected
:blob or :file => Required to point to the datastream file being added if managed content
:label => Defaults to the file name
The following are expected keys in opts hash:
opts: hash defining datastream attributes
name: name of datastream to add
====Parameters
but can also be called directly.
This object is used by [datastream_name]_append helper to add a named datastream

** EXPERIMENTAL **
def add_named_datastream(name,opts={})
  unless named_datastreams_desc.has_key?(name) && named_datastreams_desc[name].has_key?(:type) 
    raise "Failed to add datastream. Named datastream #{name} not defined for object #{pid}." 
  end
  if opts.has_key?(:mime_type)
    opts.merge!({:content_type=>opts[:mime_type]})
  elsif opts.has_key?(:mimeType)
    opts.merge!({:content_type=>opts[:mimeType]})
  end
  opts.merge!(named_datastreams_desc[name])
    
  label = opts.has_key?(:label) ? opts[:label] : ""
  #only do these steps for managed datastreams
  unless (opts.has_key?(:controlGroup)&&opts[:controlGroup]!="M")
    if opts.has_key?(:file)
      opts.merge!({:blob => opts[:file]}) 
      opts.delete(:file)
    end
    
    raise "You must define parameter blob for this managed datastream to load for #{pid}" unless opts.has_key?(:blob)
    
    #if no explicit label and is a file use original file name for label
    if !opts.has_key?(:label)&&opts[:blob].respond_to?(:original_filename)
      label = opts[:blob].original_filename
    end
    
    if opts[:blob].respond_to?(:content_type)&&!opts[:blob].content_type.nil? && !opts.has_key?(:content_type)
      opts.merge!({:content_type=>opts[:blob].content_type})
    end
    raise "The blob must respond to content_type or the hash must have :content_type or :mime_type property set" unless opts.has_key?(:content_type)
    
    #throw error for mimeType mismatch
    if named_datastreams_desc[name].has_key?(:mimeType) && !named_datastreams_desc[name][:mimeType].eql?(opts[:content_type])
      raise "Content type mismatch for add datastream #{name} to #{pid}.  Expected: #{named_datastreams_desc[name][:mimeType]}, Actual: #{opts[:content_type]}"
    end
  else 
    label = opts[:dsLocation] if (opts.has_key?(:dsLocation)) 
  end
  
  opts.merge!(:dsLabel => label)
  
  ds = create_datastream(named_datastreams_desc[name][:type], opts[:dsid], opts)
  #Must be of type datastream
  assert_kind_of 'datastream',  ds, ActiveFedora::Datastream
  #make sure dsid is nil so that it uses the prefix for mapping purposes
  #check dsid works for the prefix if it is set
  if !ds.dsid.nil? && opts.has_key?(:prefix)
    raise "dsid supplied does not conform to pattern #{opts[:prefix]}[number]" unless ds.dsid =~ /#{opts[:prefix]}[0-9]/
  end
  
  add_datastream(ds,opts)
end

def add_named_file_datastream(name, file, opts={})

opts: Options hash. See +add_named_datastream+ for expected keys and values
file: The file to add for this datastream
name: Datastream name
====Parameters
Calls add_named_datastream while assuming it will be managed content and sets :blob and :controlGroup values accordingly

** EXPERIMENTAL **
def add_named_file_datastream(name, file, opts={})
  opts.merge!({:blob=>file,:controlGroup=>'M'})
  add_named_datastream(name,opts)
end

def assert_kind_of(n, o,t)

t: The class type to check is kind_of?
o: The object to test
n: Name of object
====Parameters
Throws an assertion failure unless the object 'o' is kind_of? class 't'

** EXPERIMENTAL **
def assert_kind_of(n, o,t)
  raise "Assertion failure: #{n}: #{o} is not of type #{t}" unless o.kind_of?(t)
end

def datastream_names

Returns array of datastream names defined for this object

** EXPERIMENTAL **
def datastream_names
  named_datastreams_desc.keys
end

def inherited_with_datastream_collections(kls) #:nodoc:

:nodoc:
def inherited_with_datastream_collections(kls) #:nodoc:
  ## Do some inheritance logic that doesn't override Base.inherited
  inherited_without_datastream_collections kls
  kls.class_named_datastreams_desc = kls.class_named_datastreams_desc.dup
end

def is_named_datastream?(name)

Returns true if the name is a defined named datastream

** EXPERIMENTAL **
def is_named_datastream?(name)
  named_datastreams_desc.has_key?(name)
end

def named_datastreams

{"external_images"=>[],"thumbnails"=>{#Returns

has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
has_datastream :name=>"minivan", :prefix => "VAN", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'M'
For the following has_datastream entries and a datastream defined for minivan only would be
====Example
array of datastream objects that have been added
Returns hash of datastream names defined by has_datastream calls mapped to

** EXPERIMENTAL **
def named_datastreams
  ds_values = {}
  self.class.class_named_datastreams_desc.keys.each do |name|
    ds_values.merge!({name=>self.send("#{name}")})
  end
  return ds_values
end

def named_datastreams_desc

This hash is later used when adding a named datastream such as an "audio_file" as defined above.

"external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
{"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
The above examples result in the following hash

has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"

ActiveFedora::Base child class.
Accessor for class variable for hash that stores arguments passed to has_datastream calls within an

** EXPERIMENTAL **
def named_datastreams_desc
  self.class_named_datastreams_desc ||= {}
end

def named_datastreams_desc

This hash is later used when adding a named datastream such as an "audio_file" as defined above.

"external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
{"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
The above examples result in the following hash

has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"

ActiveFedora::Base child class.
Returns the hash that stores arguments passed to has_datastream calls within an

** EXPERIMENTAL **
def named_datastreams_desc
  @named_datastreams_desc ||= self.class.class_named_datastreams_desc
end

def named_datastreams_desc_from_class

Deprecated
It is used to initialize the value returned by public named_datastreams_desc method
Get class variable hash that stores has_datastream arguments.

** EXPERIMENTAL **
def named_datastreams_desc_from_class
  logger.warn "named_datastreams_desc_from_class is deprecated and will be removed in the next version"
  self.class.class_named_datastreams_desc
end

def named_datastreams_ids

{"thumbnails=>["THUMB1", "THUMB2"]}
It would then return

has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'

For the following has_datastream call, assume we have added two datastreams.
=== Example
of dsid's for named datastream objects
Returns hash of datastream names mapped to an array

** EXPERIMENTAL **
def named_datastreams_ids
  dsids = {}
  self.class.class_named_datastreams_desc.keys.each do |name|
    dsid_array = self.send("#{name}_ids")
    dsids[name] = dsid_array
  end
  return dsids
end 

def update_named_datastream(name, opts={})

but could change to allow metadata only updates as well
Currently requires you to update file if a managed datastream
====TODO

except the :dsid key is now required.
Update an existing named datastream. It has same parameters as add_named_datastream

** EXPERIMENTAL **
def update_named_datastream(name, opts={})
  #check that dsid provided matches existing datastream with that name
  raise "You must define parameter dsid for datastream to update for #{pid}" unless opts.include?(:dsid)
  raise "Datastream with name #{name} and dsid #{opts[:dsid]} does not exist for #{pid}" unless self.send("#{name}_ids").include?(opts[:dsid])
  add_named_datastream(name,opts)
end