traject-marc4j_reader 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 781377f3f2adb35b8345224b0d04f424355b899f
4
+ data.tar.gz: 5941faff6ae8c86282d1acad116c2079f6840d65
5
+ SHA512:
6
+ metadata.gz: 2deabe30e44a00b5f232660bf36db22bddeb0f0577ba75bfaa662d5ad98aeaca36493b2d69104e7ab3b6362b1f1ac2daf76a6c5cccd31cb0e4bae9a362901468
7
+ data.tar.gz: b9259af133a130101c0b3e6a2a47bd4eac2a6dac790c79ff9fea3062cf1ab8222873761684c641d2a7145dab20ed8a6acf263e054be925575d17c15bebe39815
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in traject_marc4j_reader.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Bill Dueber
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Traject::Marc4JReader
2
+
3
+ **Note**: `Traject::Marc4JReader` is for JRuby only.
4
+
5
+ `Traject::Marc4JReader` is a reader for the [traject](https://github.com/traject-project/traject) ETL system
6
+ that allows the use of [marc4j](https://github.com/marc4j/marc4j) as a reader when dealing with MARC
7
+ binary or MARC-XML files. It is of no use outside of `traject` run under JRuby.
8
+
9
+ It leverages [marc-marc4j](https://github.com/billdueber/ruby-marc-marc4j), which is a paper-thin wrapper around
10
+ the Marc4J `.jar` that is shipped with it.
11
+
12
+ **The output of the reader is a vanilla ruby-marc object**. You can hang onto the
13
+ original marc4j java object with the `marc4j_reader.keep_marc4j` setting.
14
+
15
+
16
+ ## Why use this?
17
+
18
+
19
+ The biggest reason would be for faster MARC/MARC-XML parsing and generation than the vanilla
20
+ [marc](https://github.com/ruby-marc/ruby-marc) gem can provide, or if you need to do something wacky with the marc4j
21
+ internal structure (such as feed it to legacy java code you have lying around).
22
+
23
+ In general, the marc4j library will parse marc21 (binary) and MARC-XML roughly twice
24
+ as fast as the pure-ruby library. While MARC parsing tends to not be a huge part
25
+ of the workload in a traject run, you'll almost certainly see performance gains.
26
+
27
+ ## Installation
28
+
29
+ Add this line to your application's Gemfile:
30
+
31
+ gem 'traject-marc4j_reader'
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install traject-marc4j_reader
40
+
41
+ ## Traject::Marc4jReader settings
42
+
43
+ For more about the traject `settings` object, see [the traject settings documentation](https://github.com/traject-project/traject/blob/master/doc/settings.md)
44
+
45
+
46
+ Note that the standard Marc4JReader always converts to UTF8,
47
+ so output will always reflect that conversion.
48
+
49
+ * `marc4j.jar_dir`: Path to a directory containing Marc4J jar file to use. All .jar's in dir will
50
+ be loaded. If unset, uses marc4j.jar bundled with traject.
51
+
52
+ * `marc4j_reader.permissive`: Used by Marc4JReader only when marc.source_type is 'binary', boolean, argument to the underlying MarcPermissiveStreamReader. Default true.
53
+
54
+ * `marc4j_reader.source_encoding`: Used by Marc4JReader only when marc.source_type is 'binary', encoding strings accepted
55
+ by marc4j MarcPermissiveStreamReader. Default "BESTGUESS", also "UTF-8", "MARC"
56
+
57
+ * `marc4j_reader.keep_marc4j`: After translating the marc4j record into a normal ruby-marc object,
58
+ provides access to the former via `record#original_marc4j`.
59
+
60
+
61
+ ## Sample use
62
+
63
+ A simple example that reads in via marc4j and outputs to the newline-delimited-json writer.
64
+
65
+ Use would be:
66
+
67
+ ```bash
68
+ traject -c id_title.rb my_marc_file.mrc
69
+ ```
70
+
71
+ ```ruby
72
+ # File id_title.rb
73
+
74
+ require 'traject'
75
+ require 'traject/marc4j_reader'
76
+ require 'traject/json_writer'
77
+
78
+ require 'traject/macros/marc21_semantics'
79
+ extend Traject::Macros::Marc21Semantics
80
+
81
+ settings do
82
+ provide "reader_class_name", "Traject::Marc4JReader"
83
+ provide "marc4j_reader.keep_marc4j", true
84
+ provide "writer_class_name", "Traject::JsonWriter"
85
+ provide "output_file", "ids_and_titles.ndj"
86
+ end
87
+
88
+ to_field "id", extract_marc("001", :first => true)
89
+ to_field "title", extract_marc_filing_version('245abdefghknp', :include_original => true)
90
+
91
+ ```
92
+
93
+
94
+ ## Contributing
95
+
96
+ 1. Fork it ( https://github.com/[my-github-username]/traject_marc4j_reader/fork )
97
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
98
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
99
+ 4. Push to the branch (`git push origin my-new-feature`)
100
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = 'test/**/*_test.rb'
8
+ t.libs.push 'test', 'test_support'
9
+ end
@@ -0,0 +1,5 @@
1
+ module Traject
2
+ class Marc4JReader
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,153 @@
1
+ require 'traject'
2
+ require 'marc'
3
+ require 'marc/marc4j'
4
+
5
+ # `Traject::Marc4JReader` uses the marc4j java package to parse the MARC records
6
+ # into standard ruby-marc MARC::Record objects. This reader may be faster than
7
+ # Traject::MarcReader, especially for XML.
8
+ #
9
+ # Marc4JReader can read MARC ISO 2709 ("binary") or MARCXML. We use the Marc4J MarcPermissiveStreamReader
10
+ # for reading binary, but sometimes in non-permissive mode, according to settings. We use the Marc4j MarcXmlReader
11
+ # for reading xml. The actual code for dealing with Marc4J is in the separate
12
+ # [marc-marc4j gem](https://github.com/billdueber/ruby-marc-marc4j).
13
+ #
14
+ # See also the pure ruby Traject::MarcReader as an alternative, if you need to read
15
+ # marc-in-json, or if you don't need binary Marc8 support, it may in some cases
16
+ # be faster.
17
+ #
18
+ # ## Settings
19
+ #
20
+ # * marc_source.type: serialization type. default 'binary', also 'xml' (TODO: json/marc-in-json)
21
+ #
22
+ # * marc4j_reader.permissive: default true, false to turn off permissive reading. Used as
23
+ # value to 'permissive' arg of MarcPermissiveStreamReader constructor.
24
+ # Only used for 'binary'
25
+ #
26
+ # * marc_source.encoding: Only used for 'binary', otherwise always UTF-8.
27
+ # String of the values MarcPermissiveStreamReader accepts:
28
+ # * BESTGUESS (default: not entirely clear what Marc4J does with this)
29
+ # * ISO-8859-1 (also accepted: ISO8859_1)
30
+ # * UTF-8
31
+ # * MARC-8 (also accepted: MARC8)
32
+ # Default 'BESTGUESS', but HIGHLY recommend setting
33
+ # to avoid some Marc4J unpredictability, Marc4J "BESTGUESS" can be unpredictable
34
+ # in a variety of ways.
35
+ # (will ALWAYS be transcoded to UTF-8 on the way out. We insist.)
36
+ #
37
+ # * marc4j_reader.jar_dir: Path to a directory containing Marc4J jar file to use. All .jar's in dir will
38
+ # be loaded. If unset, uses marc4j.jar bundled with traject.
39
+ #
40
+ # * marc4j_reader.keep_marc4j: Keeps the original marc4j record accessible from
41
+ # the eventual ruby-marc record via record#original_marc4j. Intended for
42
+ # those that have legacy java code for which a marc4j object is needed. .
43
+ #
44
+ #
45
+ # ## Example
46
+ #
47
+ # In a configuration file:
48
+ #
49
+ # require 'traject/marc4j_reader
50
+ # settings do
51
+ # provide "reader_class_name", "Traject::Marc4JReader"
52
+ #
53
+ # #for MarcXML:
54
+ # # provide "marc_source.type", "xml"
55
+ #
56
+ # # Or instead for binary:
57
+ # provide "marc4j_reader.permissive", true
58
+ # provide "marc_source.encoding", "MARC8"
59
+ # end
60
+ class Traject::Marc4JReader
61
+ include Enumerable
62
+
63
+ attr_reader :settings, :input_stream
64
+
65
+ def initialize(input_stream, settings)
66
+ @settings = Traject::Indexer::Settings.new settings
67
+ @input_stream = input_stream
68
+
69
+ if @settings['marc4j_reader.keep_marc4j'] &&
70
+ ! (MARC::Record.instance_methods.include?(:original_marc4j) &&
71
+ MARC::Record.instance_methods.include?(:"original_marc4j="))
72
+ MARC::Record.class_eval('attr_accessor :original_marc4j')
73
+ end
74
+
75
+ # Creating a converter will do the following:
76
+ # - nothing, if it detects that the marc4j jar is already loaded
77
+ # - load all the .jar files in settings['marc4j_reader.jar_dir'] if set
78
+ # - load the marc4j jar file bundled with MARC::MARC4J otherwise
79
+
80
+ @converter = MARC::MARC4J.new(:jardir => settings['marc4j_reader.jar_dir'], :logger => logger)
81
+
82
+ # Convenience
83
+ java_import org.marc4j.MarcPermissiveStreamReader
84
+ java_import org.marc4j.MarcXmlReader
85
+
86
+ end
87
+
88
+
89
+ def internal_reader
90
+ @internal_reader ||= create_marc_reader!
91
+ end
92
+
93
+ def input_type
94
+ # maybe later add some guessing somehow
95
+ settings["marc_source.type"]
96
+ end
97
+
98
+ def specified_source_encoding
99
+ #settings["marc4j_reader.source_encoding"]
100
+ enc = settings["marc_source.encoding"]
101
+
102
+ # one is standard for ruby and we want to support,
103
+ # the other is used by Marc4J and we have to pass it to Marc4J
104
+ enc = "ISO8859_1" if enc == "ISO-8859-1"
105
+
106
+ # default
107
+ enc = "BESTGUESS" if enc.nil? || enc.empty?
108
+
109
+ return enc
110
+ end
111
+
112
+ def create_marc_reader!
113
+ case input_type
114
+ when "binary"
115
+ permissive = settings["marc4j_reader.permissive"].to_s == "true"
116
+
117
+ # #to_inputstream turns our ruby IO into a Java InputStream
118
+ # third arg means 'convert to UTF-8, yes'
119
+ MarcPermissiveStreamReader.new(input_stream.to_inputstream, permissive, true, specified_source_encoding)
120
+ when "xml"
121
+ MarcXmlReader.new(input_stream.to_inputstream)
122
+ else
123
+ raise IllegalArgument.new("Unrecgonized marc_source.type: #{input_type}")
124
+ end
125
+ end
126
+
127
+ def each
128
+ while (internal_reader.hasNext)
129
+ begin
130
+ marc4j = internal_reader.next
131
+ rubymarc = @converter.marc4j_to_rubymarc(marc4j)
132
+ if @settings['marc4j_reader.keep_marc4j']
133
+ rubymarc.original_marc4j = marc4j
134
+ end
135
+ rescue Exception =>e
136
+ msg = "MARC4JReader: Error reading MARC, fatal, re-raising"
137
+ if marc4j
138
+ msg += "\n 001 id: #{marc4j.getControlNumber}"
139
+ end
140
+ msg += "\n #{Traject::Util.exception_to_log_message(e)}"
141
+ logger.fatal msg
142
+ raise e
143
+ end
144
+
145
+ yield rubymarc
146
+ end
147
+ end
148
+
149
+ def logger
150
+ @logger ||= (settings[:logger] || Yell.new(STDERR, :level => "gt.fatal")) # null logger)
151
+ end
152
+
153
+ end
@@ -0,0 +1,138 @@
1
+ # Encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ require 'traject'
6
+ require 'traject/indexer'
7
+ require 'traject/marc4j_reader'
8
+
9
+ require 'marc'
10
+
11
+ describe "Marc4JReader" do
12
+ it "reads Marc binary" do
13
+ file = File.new(support_file_path("test_data.utf8.mrc"))
14
+ settings = Traject::Indexer::Settings.new() # binary type is default
15
+ reader = Traject::Marc4JReader.new(file, settings)
16
+
17
+ array = reader.to_a
18
+
19
+ assert_equal 30, array.length
20
+ first = array.first
21
+
22
+ assert_kind_of MARC::Record, first
23
+ assert_equal first['245']['a'].encoding.name, "UTF-8"
24
+ end
25
+
26
+ it "can skip a bad subfield code" do
27
+ file = File.new(support_file_path("bad_subfield_code.marc"))
28
+ settings = Traject::Indexer::Settings.new() # binary type is default
29
+ reader = Traject::Marc4JReader.new(file, settings)
30
+
31
+ array = reader.to_a
32
+
33
+ assert_equal 1, array.length
34
+ assert_kind_of MARC::Record, array.first
35
+ assert_length 2, array.first['260'].subfields
36
+ end
37
+
38
+ it "reads Marc binary in Marc8 encoding" do
39
+ file = File.new(support_file_path("one-marc8.mrc"))
40
+ settings = Traject::Indexer::Settings.new("marc_source.encoding" => "MARC8")
41
+ reader = Traject::Marc4JReader.new(file, settings)
42
+
43
+ array = reader.to_a
44
+
45
+ assert_length 1, array
46
+
47
+
48
+ assert_kind_of MARC::Record, array.first
49
+ a245a = array.first['245']['a']
50
+
51
+ assert a245a.encoding.name, "UTF-8"
52
+ assert a245a.valid_encoding?
53
+ # marc4j converts to denormalized unicode, bah. Although
54
+ # it's legal, it probably looks weird as a string literal
55
+ # below, depending on your editor.
56
+ assert_equal "Por uma outra globalização :", a245a
57
+
58
+ # Set leader byte to proper for unicode
59
+ assert_equal 'a', array.first.leader[9]
60
+ end
61
+
62
+
63
+ it "reads XML" do
64
+ file = File.new(support_file_path "test_data.utf8.marc.xml")
65
+ settings = Traject::Indexer::Settings.new("marc_source.type" => "xml")
66
+ reader = Traject::Marc4JReader.new(file, settings)
67
+
68
+ array = reader.to_a
69
+
70
+ assert_equal 30, array.length
71
+
72
+ first = array.first
73
+
74
+ assert_kind_of MARC::Record, first
75
+ assert first['245']['a'].encoding.name, "UTF-8"
76
+ assert_equal "Fikr-i Ayāz /", first['245']['a']
77
+ end
78
+
79
+ it "keeps marc4j object when asked" do
80
+ file = File.new(support_file_path "test_data.utf8.marc.xml")
81
+ settings = Traject::Indexer::Settings.new("marc_source.type" => "xml", 'marc4j_reader.keep_marc4j' => true)
82
+ record = Traject::Marc4JReader.new(file, settings).to_a.first
83
+ assert_kind_of MARC::Record, record
84
+ assert_kind_of Java::org.marc4j.marc.impl::RecordImpl, record.original_marc4j
85
+ end
86
+
87
+ it "replaces unicode character reference in Marc8 transcode" do
88
+ file = File.new(support_file_path "escaped_character_reference.marc8.marc")
89
+ # due to marc4j idiosyncracies, this test will NOT pass with default source_encoding
90
+ # of "BESTGUESS", it only works if you explicitly set to MARC8. Doh.
91
+ settings = Traject::Indexer::Settings.new("marc_source.encoding" => "MARC8") # binary type is default
92
+ record = Traject::Marc4JReader.new(file, settings).to_a.first
93
+
94
+ assert_equal "Rio de Janeiro escaped replacement char: \uFFFD .", record['260']['a']
95
+ end
96
+
97
+ describe "Marc4J Java Permissive Stream Reader" do
98
+ # needed for sanity check when our tests fail to see if Marc4J
99
+ # is not behaving how we think it should.
100
+ it "converts character references" do
101
+ settings = Traject::Indexer::Settings.new("marc4j_reader.permissive" => true)
102
+
103
+ file = File.new(support_file_path "escaped_character_reference.marc8.marc")
104
+ reader = MarcPermissiveStreamReader.new(file.to_inputstream, true, true, "MARC-8")
105
+ record = reader.next
106
+
107
+ field = record.getVariableField("260")
108
+ subfield = field.getSubfield('a'.ord)
109
+ value = subfield.getData
110
+
111
+ assert_equal "Rio de Janeiro escaped replacement char: \uFFFD .", value
112
+ end
113
+ end
114
+
115
+ it "replaces bad byte in UTF8 marc" do
116
+
117
+ # Note this only works because the marc file DOES correctly
118
+ # have leader byte 9 set to 'a' for UTF8, otherwise Marc4J can't do it.
119
+ file = File.new(support_file_path "bad_utf_byte.utf8.marc")
120
+
121
+ # marc4j doesn't do this in permissive mode
122
+ settings = Traject::Indexer::Settings.new("marc4j_reader.permissive" => false)
123
+ reader = Traject::Marc4JReader.new(file, settings)
124
+
125
+ record = reader.to_a.first
126
+
127
+ value = record['300']['a']
128
+
129
+ assert_equal value.encoding.name, "UTF-8"
130
+ assert value.valid_encoding?, "Has valid encoding"
131
+ assert_equal "This is a bad byte: '\uFFFD' and another: '\uFFFD'", record['300']['a']
132
+ end
133
+
134
+
135
+
136
+
137
+
138
+ end
@@ -0,0 +1,88 @@
1
+ gem 'minitest' # I feel like this messes with bundler, but only way to get minitest to shut up
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+
5
+ require 'traject'
6
+ require 'marc'
7
+
8
+ # keeps things from complaining about "yell-1.4.0/lib/yell/adapters/io.rb:66 warning: syswrite for buffered IO"
9
+ # for reasons I don't entirely understand, involving yell using syswrite and tests sometimes
10
+ # using $stderr.puts. https://github.com/TwP/logging/issues/31
11
+ STDERR.sync = true
12
+
13
+ # Hacky way to turn off Indexer logging by default, say only
14
+ # log things higher than fatal, which is nothing.
15
+ require 'traject/indexer/settings'
16
+ Traject::Indexer::Settings.defaults["log.level"] = "gt.fatal"
17
+
18
+ def support_file_path(relative_path)
19
+ return File.expand_path(File.join("test_support", relative_path), File.dirname(__FILE__))
20
+ end
21
+
22
+ # The 'assert' method I don't know why it's not there
23
+ def assert_length(length, obj, msg = nil)
24
+ unless obj.respond_to? :length
25
+ raise ArgumentError, "object with assert_length must respond_to? :length", obj
26
+ end
27
+
28
+
29
+ msg ||= "Expected length of #{obj} to be #{length}, but was #{obj.length}"
30
+
31
+ assert_equal(length, obj.length, msg.to_s )
32
+ end
33
+
34
+ def assert_start_with(start_with, obj, msg = nil)
35
+ msg ||= "expected #{obj} to start with #{start_with}"
36
+
37
+ assert obj.start_with?(start_with), msg
38
+ end
39
+
40
+
41
+ # An empty record, for making sure extractors and macros work when
42
+ # the fields they're looking for aren't there
43
+
44
+ def empty_record
45
+ rec = MARC::Record.new
46
+ rec.append(MARC::ControlField.new('001', '000000000'))
47
+ rec
48
+ end
49
+
50
+ # pretends to be a SolrJ HTTPServer-like thing, just kind of mocks it up
51
+ # and records what happens and simulates errors in some cases.
52
+ class MockSolrServer
53
+ attr_accessor :things_added, :url, :committed, :parser, :shutted_down
54
+
55
+ def initialize(url)
56
+ @url = url
57
+ @things_added = []
58
+ @add_mutex = Mutex.new
59
+ end
60
+
61
+ def add(thing)
62
+ @add_mutex.synchronize do # easy peasy threadsafety for our mock
63
+ if @url == "http://no.such.place"
64
+ raise org.apache.solr.client.solrj.SolrServerException.new("mock bad uri", java.io.IOException.new)
65
+ end
66
+
67
+ # simulate a multiple id error please
68
+ if [thing].flatten.find {|doc| doc.getField("id").getValueCount() != 1}
69
+ raise org.apache.solr.client.solrj.SolrServerException.new("mock non-1 size of 'id'")
70
+ else
71
+ things_added << thing
72
+ end
73
+ end
74
+ end
75
+
76
+ def commit
77
+ @committed = true
78
+ end
79
+
80
+ def setParser(parser)
81
+ @parser = parser
82
+ end
83
+
84
+ def shutdown
85
+ @shutted_down = true
86
+ end
87
+
88
+ end
@@ -0,0 +1 @@
1
+ 00582cam 2200217 4500001000800000005001700008008004100025035001200066035001400078035001900092035002300111040001300134049000800147050002000155100001800175245003300193260003900226300001000265910002600275991006300301117499920020328125800.0670101s1959 fr 000 0 fre  a1174999 aABK6934EI a(MdBJ)99005528 a(CaOTULAS)00190819 aOTUbENG00aJHE 4aPQ2605 A873bC61 aCayrol, Jean.14aLes corps �etrangers, roman. aParis��bEditions du Seuilc[1959] a188p. a1174999bHorizon bib# aPH4605.C36 C6 1959flcbelccc. 1q0i2240984leoffsmelsc
@@ -0,0 +1 @@
1
+ 00083 a2200037 4500300004500000 aThis is a bad byte: '�' and another: '�'
@@ -0,0 +1 @@
1
+ 00138cam 2200049Ia 45000010008000002600080000082196384 aRio de Janeiro escaped replacement char: &#xFFFD; .bEditora Record,c2000.
@@ -0,0 +1 @@
1
+ 00663cam 2200217Ia 4500001000800000005001700008008004100025020001500066035001600081040001300097049000900110090002200119100002000141245010100161260004500262300002100307650001800328910002600346991006100372994001200433219638420010919155148.0010626s2000 bl 000 0 por d a8501058785 aocm47198253 aIXAcIXA aJHEE aJZ1308b.S26 20001 aSantos, M�ilton10aPor uma outra globaliza�c�ao :bdo pensamemto �unico �a consci�encia universal /cMilton Santos. aRio de Janeiro :bEditora Record,c2000. a174 p. ;c21 cm. 0aGlobalization a2196384bHorizon bib# aJZ1308.S26 2000flcbelccc. 1q0i3557489lemainmemsel aX0bJHE