Class: XMLCodec::XMLElement

Inherits:
Object
  • Object
show all
Defined in:
lib/element.rb

Overview

This class should be inherited from to create classes that are able to import and export XML elements and their children. It provides three main functions: xmlattr, xmlsubel and xmlsubel_mult.

To create an importer/exporter for a XML format all that’s needed is to create a class for each of the elements and then declare their atributes and subelements.

Two other functions have an important role. elname declares the name of the XML element the class represents. elwithvalue declares that the element has no subelements and includes only text content.

After the class is defined import_xml can be used to import the content from a REXML Element or Document and create_xml can be used to create the XML DOM of the element as a child to a REXML Element or Document. For big documents these are usually too slow and memory hungry, using xml_text to export to XML and import_xml_text to import XML are probably better ideas. import_xml_text is just a utility function around XMLStreamObjectParser, that allow more flexible stream parsing of XML files while still using the same XMLElement objects.

WARNING: This API is still very much a work in progress and very rough in certain places. Changes will surely be made.

Constant Summary collapse

INDENT_STR =
'  '
CACHE =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#__parentObject

Returns the value of attribute __parent.



33
34
35
# File 'lib/element.rb', line 33

def __parent
  @__parent
end

#__xml_textObject

Returns the value of attribute __xml_text.



32
33
34
# File 'lib/element.rb', line 32

def __xml_text
  @__xml_text
end

#element_idObject

Returns the value of attribute element_id.



32
33
34
# File 'lib/element.rb', line 32

def element_id
  @element_id
end

#parent_idObject

Returns the value of attribute parent_id.



32
33
34
# File 'lib/element.rb', line 32

def parent_id
  @parent_id
end

Class Method Details

.get_element_class(name) ⇒ Object

Gets the class for a certain element name.



339
340
341
342
343
344
345
# File 'lib/element.rb', line 339

def self.get_element_class(name)
  cl = elclasses[name.to_sym]
 if not cl
  raise ElementClassNotFound, "No class defined for element type: '" + name.to_s + "'"
end
cl
end

.get_element_names(name) ⇒ Object

Gets the possible element names for a certain element.



348
349
350
# File 'lib/element.rb', line 348

def self.get_element_names(name)
  get_element_class(name).get_elnames
end

.import_xml(xmlel) ⇒ Object

Import the XML into an object from a REXML element. This call is recursive and imports any subelements found into the corresponding objects.



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/element.rb', line 382

def self.import_xml(xmlel)
  if xmlel.is_a? REXML::Document
    xmlel = xmlel.root
  end
  
  elements = []
  xmlel.to_a.each do |e|
    if e.is_a? REXML::Text
      elements << e.value
    else
      elclass = get_element_class(e.name)
      elements << elclass.import_xml(e)
    end
  end
  
  attributes = {}
  xmlel.attributes.each do |name, value|
    attributes[name] = value
  end
  
  new_with_content(attributes, elements)
end

.import_xml_text(text) ⇒ Object

Import the XML directly from the text. This call receives the text and the classes that should be used to import the subelements.



407
408
409
410
411
# File 'lib/element.rb', line 407

def self.import_xml_text(text)
  parser = XMLStreamObjectParser.new(self)
  parser.parse(text)
  parser.top_element
end

.new_with_content(attrs, children) ⇒ Object

Create a new element passing it all the atributes, children and texts



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/element.rb', line 414

def self.new_with_content(attrs, children)
  text_children = []
  element_children = []
  
  children.each do |c|
    if c.is_a? String
      text_children << c
    else
      element_children << c
    end
  end

  obj = self.allocate
  obj.add_attr(attrs)
  obj.add_subel(element_children)
  obj.add_texts(text_children)
  if obj.has_subelements?
    obj.add_subelements(children)
  end
  obj
end

Instance Method Details

#add_attr(attrs) ⇒ Object

add the attributes passed as a hash to the element



437
438
439
440
441
# File 'lib/element.rb', line 437

def add_attr(attrs)
  attrs.each do |name, value|
    self.send("#{name}=", value)
  end
end

#add_subel(children) ⇒ Object

add the subelements into the element



451
452
453
454
455
456
457
458
459
460
461
# File 'lib/element.rb', line 451

def add_subel(children)
  children.each do |c|
    if subel_name = get_subel(c.class)
      if self.class.subel_mult? subel_name
        self.send(subel_name) <<  c
      else
        self.send(subel_name.to_s+'=', c)
      end 
    end
  end
end

#add_subelements(all_children) ⇒ Object

If the class is one with many subelements import all of them into the object.



465
466
467
# File 'lib/element.rb', line 465

def add_subelements(all_children)
  all_children.each {|c| self.subelements << c}
end

#add_texts(texts) ⇒ Object

add the text elements into the element



444
445
446
447
448
# File 'lib/element.rb', line 444

def add_texts(texts)
  if hasvalue?
    @value = texts.join
  end
end

#create_xml(parent) ⇒ Object

Creates the xml for the element inside the parent element. The parent passed should be a REXML element or document. This call is recursive creating the XML for any subelements.



365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/element.rb', line 365

def create_xml(parent)
  xmlel = parent.add_element self.elname.to_s
  if self.hasvalue?
    xmlel.text = self.value
  end
  create_xml_attr(xmlel)
  create_xml_subel(xmlel)
  
  if has_subelements?
    create_xml_subelements(xmlel)
  end
  
  xmlel
end

#delete_element(element) ⇒ Object

Remove the given subelement from the element



312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/element.rb', line 312

def delete_element(element)
  self.class.each_subel do |a|  
    value = self.send(a)
    if self.class.subel_mult? a
      value.delete_element(element)
    else
      self.send(a.to_s+'=', nil) if value == element
    end
  end
  
  if has_subelements?
    @subelements.delete_element(element)
  end 
end

#end_partial_export(file) ⇒ Object

Ends the partial exporting of the element.



518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/element.rb', line 518

def end_partial_export(file)
  if not already_partial_export_ended?
    @already_partial_export_ended = true
    
    if not already_partial_exported?
      raise "<#{self} Trying to end the export of an element that hasn't"+
            " been started yet"
    end
    
    each_subelement do |e|
      e.end_partial_export(file)
    end
    
    file << create_close_tag

    if self.__parent
      self.__parent.delete_element(self)
    end
  end
end

#has_subelements?Boolean

Method that checks if a given class has subelements. This is usually only used when exporting stuff.

Returns:

  • (Boolean)


354
# File 'lib/element.rb', line 354

def has_subelements?; false; end

#hasvalue?Boolean

tests if the element is a value element as defined by ‘elwithvalue’

Returns:

  • (Boolean)


357
358
359
# File 'lib/element.rb', line 357

def hasvalue?
  false
end

#partial_export(file) ⇒ Object

Export this element into a file. Will also start to export the parents of the element. It’s equivalent to calling start_partial_export followed by end_partial_export.



489
490
491
492
493
494
# File 'lib/element.rb', line 489

def partial_export(file)
  if not already_partial_exported?
    start_partial_export(file)
    end_partial_export(file)
  end
end

#start_partial_export(file) ⇒ Object

Starts to export the element to a file. all the existing elements will be exported. After calling this you should only add stuff that you will export explicitly by calling partial_export or start_partial_export.



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/element.rb', line 499

def start_partial_export(file)
  if not already_partial_exported?
    @already_partial_exported = true
    if self.__parent
      self.__parent.start_partial_export(file)
    end
    
    file << create_open_tag
    if self.hasvalue?
      file << XMLUtils::escape_xml(self.value)
    end
    
    each_subelement do |e|
      e.partial_export(file)
    end
  end
end

#xml_textObject

create the XML text of the element. This does not use REXML so should be pretty fast.



472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/element.rb', line 472

def xml_text
  str = create_open_tag
  if self.hasvalue?
    str << XMLUtils::escape_xml(self.value)
  end
  
  each_subelement do |e|
    str << e.xml_text
  end
  
  str << create_close_tag
  str
end