source: trunk/greenstone3-extensions/gs3build/src/org/greenstone/gsdl3/gs3build/indexers/MGIndexer.java@ 12188

Last change on this file since 12188 was 12188, checked in by kjdon, 18 years ago

Initial revision

  • Property svn:keywords set to Author Date Id Revision
File size: 22.4 KB
Line 
1package org.greenstone.gsdl3.gs3build.indexers;
2
3import java.util.List;
4import java.util.ArrayList;
5import java.util.Iterator;
6
7import java.io.File;
8import java.io.InputStream;
9import java.io.OutputStream;
10import java.io.IOException;
11import java.io.BufferedReader;
12import java.io.InputStreamReader;
13
14import org.w3c.dom.*;
15
16import org.greenstone.mg.*;
17
18import org.greenstone.gsdl3.gs3build.doctypes.DocumentID;
19import org.greenstone.gsdl3.gs3build.doctypes.DocumentInterface;
20import org.greenstone.gsdl3.gs3build.doctypes.HTMLDocument;
21import org.greenstone.gsdl3.gs3build.doctypes.METSDocument;
22import org.greenstone.gsdl3.gs3build.metadata.*;
23import org.greenstone.gsdl3.gs3build.xpointer.XPointer;
24import org.greenstone.gsdl3.gs3build.util.DOMUtils;
25import org.greenstone.gsdl3.util.GSXML;
26import org.greenstone.gsdl3.util.Misc;
27import org.greenstone.gsdl3.util.Processing;
28
29// for debug
30import org.greenstone.gsdl3.util.XMLConverter;
31
32public class MGIndexer //extends AbstractIndexer
33 implements IndexerInterface
34{
35 int pass;
36 int documentSeqNo;
37 int sectionSeqNo;
38 boolean firstDocument;
39 String outputDirectory;
40 StringBuffer indexBuffer;
41 File textDirectory;
42 File indexDirectory;
43 String indexStem;
44 String textStem;
45 List indexes;
46 String overallName;
47
48 MGIndex current_index = null;
49 MGPassesWrapper mgPasses;
50
51 static final char END_OF_DOCUMENT = (char) 2;
52 //static final char END_OF_SECTION = (char) 3; // actually this is end of para for mg - don't use this yet
53 static final char END_OF_STREAM = (char) 4;
54
55 public static final String MG_INDEX_TYPE = "mg";
56 public static final String INDEX_FILE_STEM = "index";
57
58 class MGIndex
59 {
60 String name=null;
61 String level=null;
62 List fields=null;
63 boolean error = false;// assume built until we get an error
64
65 public MGIndex(Element index_element) {
66
67 this.fields = new ArrayList();
68 this.name = index_element.getAttribute(GSXML.NAME_ATT);
69 if (this.name.equals("")) {
70 // TODO make this dynamic
71 this.name = "xx";
72 }
73 NodeList children = index_element.getChildNodes();
74 for (int c = 0; c < children.getLength(); c ++) {
75 Node child = children.item(c);
76
77 if (child.getNodeType() == Node.ELEMENT_NODE) {
78 String name = child.getNodeName();
79
80 if (name.equals(GSXML.LEVEL_ELEM)) {
81 this.level = DOMUtils.getNodeChildText(children.item(c));
82 }
83 else if (name.equals(GSXML.FIELD_ELEM)) {
84 String fieldName = DOMUtils.getNodeChildText(children.item(c));
85 this.fields.add(fieldName);
86 }
87 }
88 }
89 }
90
91 public MGIndex(String name, String level, List fields)
92 {
93 this.name = name;
94 this.level = level;
95 this.fields = fields;
96 }
97
98 // old gs2 style config - can we get rid of this??
99 public MGIndex(String indexLabel)
100 {
101 int colonAt = indexLabel.indexOf(':');
102
103 if (colonAt < 0) {
104 System.err.println("MGIndex(): invalid index specification: "+indexLabel);
105 return;
106 }
107 String field_string = indexLabel.substring(colonAt+1);
108 String [] field_list = field_string.split(",");
109 this.fields = new ArrayList();
110 for (int i=0; i<field_list.length; i++) {
111 this.fields.add(field_list[i]);
112 }
113 this.level = indexLabel.substring(0, colonAt);
114 //createIndexName
115 StringBuffer new_name = new StringBuffer();
116 new_name.append(Character.toLowerCase((char) this.level.charAt(0)));
117 int c, w;
118 w = 0;
119 c = 0;
120 while (c < field_string.length() && w < 2) {
121 char ch = field_string.charAt(c);
122
123 ch = Character.toLowerCase(ch);
124 if (Character.isLetter(ch)) {
125 if (ch != 'a' && ch != 'e' && ch != 'i' &&
126 ch != 'o' && ch != 'u') {
127 new_name.append(ch);
128 w++;
129 }
130 }
131 c ++;
132 }
133 this.name = new_name.toString();
134 }
135
136
137 public String getLevel()
138 {
139 return this.level;
140 }
141
142 public List getFields()
143 {
144 return this.fields;
145 }
146
147 public String getName()
148 {
149// if (this.name==null || this.name.equals("")) {
150// createIndexName();
151// }
152 return this.name;
153 }
154
155 public boolean hasError() {
156 return this.error;
157 }
158 public void setError(boolean b) {
159 this.error = b;
160 }
161
162// private void createIndexName() {
163// StringBuffer new_name = new StringBuffer();
164// new_name.append(Character.toLowerCase((char) this.level.charAt(0)));
165
166// int c, w;
167// w = 0;
168// c = 0;
169// String [] fields_concat = this.fields.toArray
170// while (c < this.field.length() && w < 2) {
171// char ch = this.field.charAt(c);
172
173// ch = Character.toLowerCase(ch);
174// if (Character.isLetter(ch)) {
175// if (ch != 'a' && ch != 'e' && ch != 'i' &&
176// ch != 'o' && ch != 'u') {
177// new_name.append(ch);
178// w++;
179// }
180// }
181// c ++;
182// }
183// this.name = new_name.toString();
184// }
185
186
187 } // MGIndex
188
189 public MGIndexer(String name)
190 {
191 this.indexes = new ArrayList();
192 this.overallName = name;
193 }
194
195 public String getIndexType()
196 {
197 return MG_INDEX_TYPE;
198 }
199
200 public String getName()
201 {
202 return this.overallName;
203 }
204
205 public boolean configure(Node search_node)
206 {
207 NodeList index_children = GSXML.getChildrenByTagName(search_node, GSXML.INDEX_ELEM);
208
209 // add a text 'index' - we should be able to turn this off in the config file? actually mg needs a text index
210 ArrayList list = new ArrayList();
211 list.add("text");
212 MGIndex index = new MGIndex("text", "section", list);
213 indexes.add(index);
214 for (int i = 0; i < index_children.getLength(); i ++) {
215 Element index_elem = (Element)index_children.item(i);
216 index = new MGIndex(index_elem);
217 if (index.getName() != null && index.getLevel() != null && index.getFields()!= null) {
218
219 indexes.add(index);
220 } else {
221 System.err.println("invalid index spec, not including"+new XMLConverter().getPrettyString(index_elem));
222 }
223 }
224 // TODO make sure all index names are unique
225 return true;
226 }
227
228 /**
229 * The output directory should be (collection)/building/text/ for
230 * normal Greenstone builds.
231 *
232 * @param <code>String</code> the label to configure
233 * @param <code>String</code> the value...
234 */
235 public boolean configure(String label, String value)
236 {
237 if (label.equals(IndexerManager.outputDir)) {
238 this.outputDirectory = value;
239 this.pass = 0;
240
241 // attempt to ensure that the text subdirectory exists
242 this.textDirectory = new File(outputDirectory, "text");
243 if (!textDirectory.exists()) {
244 if (!textDirectory.mkdir()) {
245 return false;
246 }
247 }
248 else if (!textDirectory.isDirectory()) {
249 return false;
250 }
251 this.textStem = this.textDirectory.getPath() + File.separator + INDEX_FILE_STEM;
252
253 // Sign to the user which mg directory is being used...
254 System.out.println("Output MG directory is " + this.textStem);
255 }
256 else if (label.equals(IndexerInterface.GS2_INDEX_LABEL)) {
257 this.indexes.add(new MGIndex(value));
258 }
259
260 return true;
261 }
262
263
264 private Node recurseDOM(DocumentInterface metsDoc, Node node,
265 AbstractStructure structure, StringBuffer textBuffer,
266 StringBuffer extraBuffer, String namespace)
267 //String name, String namespace, String field)
268 {
269 List fields = current_index.getFields();
270 // send out the ctrl-c...if this is
271 if (structure.getStructureType().equals(METSDivision.DIVISION_TYPE)) {
272 // try doing this for all index types
273 // actually we should only need to do this once ????
274 if (this.pass == 0) {
275 //if ((this.currentIndexName != null)) { // && this.level != null && this.level.equals(IndexerInterface.SECTION_LEVEL)) { //name.startsWith("s")) {
276 METSDivision division = (METSDivision) structure;
277
278 // get the division metadata block
279 METSDescriptive descriptive;
280 String metadataId = division.getDefaultMetadataReference();
281 if (metadataId == null) {
282 descriptive = metsDoc.getDocumentMetadata().createDescriptive(division.getLabel());
283 division.addMetadataReference(descriptive.getID());
284 }
285 else {
286 // Get the descriptive item...
287 descriptive = metsDoc.getDocumentMetadata().getDescriptiveById(metadataId);
288 }
289
290 descriptive.addMetadata("gsdl3", "mgseqno", this.overallName + "." + Integer.toString(this.sectionSeqNo));
291
292 metsDoc.setChanged(true);
293 //metsDoc.setModified(true);
294 // System.out.println("Assigning " + this.sectionSeqNo + " to " + metsDoc.getID() + " " + division.getLabel());
295 } // section level
296
297 // append an 'end of section' marker
298 //textBuffer.append(END_OF_SECTION);
299 this.sectionSeqNo ++;
300
301 // for document-level indexes, always append an 'end of document' tag at the
302 // end of the document for each section. Otherwise, each section is followed
303 // by an end of document character. This ensures that all indexes use the
304 // same document numbering...
305 if (this.current_index.getLevel().equals(IndexerInterface.DOCUMENT_LEVEL)) {
306 extraBuffer.append(END_OF_DOCUMENT);
307 }
308 else {
309 textBuffer.append(END_OF_DOCUMENT);
310 this.documentSeqNo ++;
311 }
312
313 // produce the body here for metadata output of divisions - in the case of
314 // text output, that will happen below...
315
316 if (fields.size()>1 || !((String)fields.get(0)).equals("text")) {
317 // if there is only text, don't do this
318 METSDescriptive descriptive;
319
320 METSDivision division = (METSDivision) structure;
321
322 String metadataId = division.getDefaultMetadataReference();
323 // are there other metadata refs to get??
324 descriptive = metsDoc.getDocumentMetadata().getDescriptiveById(metadataId);
325 if (descriptive != null) {
326 for (int i=0; i<fields.size(); i++) {
327 String field = (String)fields.get(i);
328 if (field.equals("text")) {
329 continue;
330 }
331 List values = descriptive.getMetadata(namespace, field);
332 if (values != null) {
333 Iterator valueIter = values.iterator();
334 while (valueIter.hasNext()) {
335 String value = valueIter.next().toString();
336 textBuffer.append(value);
337 textBuffer.append(" ");
338 }
339 }
340 }
341 }
342 }
343 }
344
345 // go through our children as required...
346 Iterator children = structure.getChildIterator();
347 Node startNode;
348 boolean index_text = fields.contains("text");
349 while (children.hasNext()) {
350 AbstractStructure child = (AbstractStructure) children.next();
351
352 // get xpointer for child
353 // get start position node
354 if (metsDoc.getDocumentType() == "METS"){
355 startNode = ((METSDocument) metsDoc).getSectionStartNode((METSDivision) child);
356 } else {
357 startNode = ((HTMLDocument) metsDoc).getSectionStartNode((METSDivision) child);
358 }
359
360 // while this node isn't the child's start node, produce the
361 // HTML node text, if in text field mode...
362 if (index_text) {
363 while (node != startNode) {
364 XPointer.printNode(node, textBuffer, false);
365 node = XPointer.getNextNode(node);
366 }
367 }
368
369 // recurse to child
370 node = this.recurseDOM(metsDoc, node, child, textBuffer, extraBuffer, namespace); // name, namespace, field);
371 } // while next child
372
373 // close a document - the actual closing \B will be done by the main
374 // loop, so only a required \C is printed here...
375 // why have we got STRUCTURE_TYPE here and DIVISION_TYPE above????
376 if (structure.getStructureType().equals(METSStructure.STRUCTURE_TYPE)) {
377 if (index_text) {
378 while (node != null) {
379 XPointer.printNode(node, textBuffer, false);
380 node = XPointer.getNextNode(node);
381 }
382 }
383
384 //textBuffer.append(END_OF_SECTION);
385 this.sectionSeqNo ++;
386
387 }
388 return node;
389 }
390
391 private String prepareDOM(DocumentInterface metsDoc, Document document, METSStructure structure, String namespace)
392 {
393 // String name, String namespace, String field)
394 StringBuffer extraBuffer = new StringBuffer();
395 Node node = document.getDocumentElement();
396 StringBuffer textBuffer = new StringBuffer();
397
398 this.recurseDOM(metsDoc, node, structure, textBuffer, extraBuffer, namespace); //name, namespace, field);
399 textBuffer.append(extraBuffer.toString());
400 return textBuffer.toString();
401 }
402
403 /**
404 * Index a single document; the document interface can be used to extract individual
405 * metadata items etc. as required or desired and index those instead or as well as
406 * the body text of the document.
407 */
408 public boolean indexDocument(DocumentID docID, DocumentInterface document)
409 {
410
411 if (!this.firstDocument) {
412 this.indexBuffer.append(END_OF_DOCUMENT);
413 mgPasses.processDocument(indexBuffer.toString());
414 this.indexBuffer.delete(0, this.indexBuffer.length());
415
416 }
417
418 String docText = null;
419 // set the mgseqno if first pass
420 if (this.pass == 0) {
421 document.removeAllMetadata("gsdl3", "mgseqno");
422 document.addDocumentMetadata("gsdl3", "mgseqno", this.overallName+"."+Integer.toString(this.sectionSeqNo));
423 }
424
425 this.sectionSeqNo ++;
426
427 //long start = System.currentTimeMillis();
428 Document domDocument = document.getDOMDocument();
429 if (domDocument != null) {
430 System.err.println("dom doc is not null");
431 METSStructure sections = document.getDocumentStructure().getStructure("Section");
432 if (sections != null) {
433 System.err.println("sections are not null");
434 docText = this.prepareDOM(document, domDocument, sections, "gsdl3"); //this.name, "gsdl3", this.field);
435 // System.out.println(docText);
436 }
437 }
438 //long finish = System.currentTimeMillis();
439 //System.err.println("dom doc = "+ Long.toString(finish-start));
440 //start = System.currentTimeMillis();
441 if (docText == null) {
442 System.err.println("dom doc or sections was null - asking for doc text");
443 StringBuffer doc_text_buffer = new StringBuffer();
444 List fields = this.current_index.getFields();
445 for (int i=0; i<fields.size(); i++) {
446 String field = (String)fields.get(i);
447 if (field.equals("text")) {
448 doc_text_buffer.append(document.getDocumentText());
449 } else {
450 // its a metadata - do namespace properly!!
451 List values = document.getDocumentMetadataItem("gsdl3", field);
452 if (values != null) {
453 Iterator valueIter = values.iterator();
454 while (valueIter.hasNext()) {
455 String value = valueIter.next().toString();
456 doc_text_buffer.append(value);
457 }
458 }
459 }
460 } // for each field
461 docText = doc_text_buffer.toString();
462 sectionSeqNo ++;
463 }
464 //finish = System.currentTimeMillis();
465 //System.err.println("whole doc = "+ Long.toString(finish-start));
466
467 this.indexBuffer.append(docText);
468 // remember that we're not on the first document,
469 this.firstDocument = false;
470 this.documentSeqNo ++;
471
472 return true;
473 }
474
475 /**
476 * Initialise the pass: open required files, check status
477 */
478 public boolean startPass(int passNumber) {
479
480
481 this.pass = passNumber;
482 this.firstDocument = true;
483 this.documentSeqNo = 1;
484 this.sectionSeqNo = 1;
485
486 this.mgPasses = new MGPassesWrapper();
487 this.indexBuffer = new StringBuffer();
488 int indexNo = this.pass/2;
489 this.current_index = null;
490
491 this.current_index = (MGIndex) this.indexes.get(indexNo);
492 if (this.current_index.hasError()) {
493 // an error has already occurred for this index, don't continue
494 System.out.println("pass "+this.pass+": aborted due to errors in the previous pass");
495 return false;
496 }
497 // attempt to ensure that the text/index subdirectory exists
498 this.indexDirectory = new File(outputDirectory, current_index.getName());
499 if (!indexDirectory.exists()) {
500 if (!indexDirectory.mkdir()) {
501 return false;
502 }
503 }
504 else if (!indexDirectory.isDirectory()) {
505 return false;
506 }
507
508 this.indexStem = this.indexDirectory.getPath() + File.separatorChar + INDEX_FILE_STEM; // TODO: modify for index
509 if (this.pass == 0) {
510 // first pass, also set up the textStem
511 this.textDirectory = this.indexDirectory;
512 this.textStem = this.indexStem;
513 }
514 mgPasses.setFileName(this.indexStem);
515 if (!Misc.isWindows()) {
516 mgPasses.setBasePath("/");
517 }
518 int mgPass = this.pass < 2 ? this.pass : ((this.pass % 2) + 2);
519
520 mgPasses.setBufferSize(100000);
521
522 switch (mgPass) {
523 case 0:
524 // -b 100000 -T1
525 mgPasses.addPass(MGPassesWrapper.TEXT_PASS_1);
526
527
528 break;
529
530 case 1:
531 // -b 100000 -T2
532 mgPasses.addPass(MGPassesWrapper.TEXT_PASS_2);
533 break;
534
535 case 2:
536 // -b 100000 -2 -m 32 -s 0 -G -t 10 -N1
537 mgPasses.addPass(MGPassesWrapper.INDEX_PASS_1);
538 mgPasses.setInvfLevel(MGPassesWrapper.INVF_LEVEL_2);
539 mgPasses.setStemOptions(MGPassesWrapper.STEMMER_ENGLISH, MGPassesWrapper.NO_STEM_OR_CASE);
540 mgPasses.setInversionMemLimit(32);
541 mgPasses.ignoreSGMLTags(true);
542 break;
543
544 case 3:
545 // -b 100000 -2 -c 3 -G -t 10 -N2
546 mgPasses.addPass(MGPassesWrapper.INDEX_PASS_2);
547 mgPasses.setInvfLevel(MGPassesWrapper.INVF_LEVEL_2);
548 mgPasses.ignoreSGMLTags(true);
549 break;
550 }
551
552 mgPasses.init();
553 System.out.println("Starting Pass " + this.pass);
554 return true;
555 }
556
557 /**
558 * Complete a pass - reset file counters, close files, etc.
559 */
560 public boolean endPass(int passNumber) {
561 try {
562 this.indexBuffer.append(END_OF_DOCUMENT);
563 mgPasses.processDocument(indexBuffer.toString());
564 this.indexBuffer.delete(0, this.indexBuffer.length());
565 Thread.sleep(1000); // what for??
566 }
567 catch (InterruptedException ex) {
568 System.out.println(ex);
569 }
570 mgPasses.finish();
571 try {
572 Thread.sleep(1000);
573 } catch (Exception e) {}
574
575 int exit_value = 0;
576 System.out.println("Pass " + this.pass + " completed with " + exit_value);
577 if (exit_value !=0) {
578 //assume something has gone wrong, don't continue
579 current_index.setError(true);
580 return false;
581 }
582 int mgPass = this.pass < 2 ? this.pass : ((this.pass % 2) + 2);
583 String osextra = "";
584 if (!Misc.isWindows()) {
585 osextra = " -d / ";
586 }
587
588 switch (mgPass) {
589
590 case 0:
591 System.out.println("Compressing dictionary");
592 exit_value = Processing.runProcess("mg_compression_dict -f " + this.textStem + osextra + " -S -H -2 -k 5120");
593 if (exit_value == 0) {
594 System.out.println("Compressed dictionary successfully written");
595 } else {
596 System.err.println("Error from mg_compression_dict: " + exit_value);
597 current_index.setError(true);
598
599 return false;
600 }
601 break;
602
603 case 2:
604 System.out.println("Creating perfect hash");
605 exit_value = Processing.runProcess("mg_perf_hash_build -f " + this.indexStem+osextra);
606 if (exit_value ==0) {
607 System.out.println("Perfect hashes completed");
608 } else {
609 System.err.println("Unable to build the perfect hash");
610 current_index.setError(true);
611 return false;
612 }
613 break;
614
615 case 3:
616 System.out.println("Writing weights file");
617 exit_value = Processing.runProcess("mg_weights_build -f " + this.indexStem + " -t " + this.textStem + osextra);
618 if (exit_value ==0) {
619 System.out.println("Weights file successfully written");
620 } else {
621 System.err.println("Unable to create weights file");
622 current_index.setError(true);
623 return false;
624 }
625
626 System.out.println("Creating inverted dictionary");
627 exit_value = Processing.runProcess("mg_invf_dict -f " + this.indexStem + osextra);
628 if (exit_value ==0) {
629 System.out.println("Inverted dictionary file successfully written");
630 } else {
631 System.out.println("Unable to create inverted dictionary file");
632 current_index.setError(true);
633 return false;
634 }
635
636 System.out.println("Creating Stem indexes");
637 exit_value = Processing.runProcess("mg_stem_idx -b 4096 -s1 -f " + this.indexStem+osextra);
638 if (exit_value == 0) {
639 System.out.println("Stemmed index 1 successfully written");
640 } else {
641 System.out.println("Unable to create stemmed index 1");
642 current_index.setError(true);
643 return false;
644 }
645
646 exit_value = Processing.runProcess("mg_stem_idx -b 4096 -s2 -f " + this.indexStem+osextra);
647 if (exit_value == 0) {
648 System.out.println("Stemmed index 2 successfully written");
649 } else {
650 System.out.println("Unable to create stemmed index 2");
651 current_index.setError(true);
652 return false;
653 }
654 exit_value = Processing.runProcess("mg_stem_idx -b 4096 -s3 -f " + this.indexStem+osextra);
655 if (exit_value == 0) {
656 System.out.println("Stemmed index 3 successfully written");
657 } else {
658 System.out.println("Unable to create stemmed index 3");
659 current_index.setError(true);
660 return false;
661 }
662
663 break;
664 } // switch
665
666 mgPasses = null;
667 return true;
668 }
669
670 /**
671 * Do any tidying up
672 */
673 public void tidyup()
674 {
675 }
676
677 /**
678 * Return the number of passes required for this index.
679 */
680 public int getNumberOfPasses()
681 {
682 //return 2 + this.indexes.size() * 2;
683 return this.indexes.size()*2;
684 }
685
686 public boolean addServiceDescriptions(org.w3c.dom.Element service_rack_list)
687 {
688 Document doc = service_rack_list.getOwnerDocument();
689
690 // generate the list of indexes
691 Element index_list = doc.createElement(GSXML.INDEX_ELEM+GSXML.LIST_MODIFIER);
692 boolean found_index = false;
693 String def_index = ""; // the default index will just be the first one created for now.
694 for (int i=1; i<this.indexes.size(); i++) {
695 // start at 1: 0 will be the text index
696 MGIndex index = (MGIndex)this.indexes.get(i);
697 if (!index.hasError()) {
698 Element e = doc.createElement(GSXML.INDEX_ELEM);
699 e.setAttribute(GSXML.NAME_ATT, index.getName());
700 index_list.appendChild(e);
701 if (found_index == false) {
702 // this is the first index
703 found_index = true;
704 def_index = index.getName();
705 }
706 }
707 }
708
709 if (!found_index) {
710 // no indexes were able to be created, so we can't use them or the text
711 return false;
712 }
713 Element default_index = doc.createElement("defaultIndex");
714 default_index.setAttribute(GSXML.NAME_ATT, def_index);
715 Element base_index_name = doc.createElement("baseIndexPrefix");
716 base_index_name.setAttribute(GSXML.NAME_ATT, overallName);
717 Element index_stem = doc.createElement("indexStem");
718 index_stem.setAttribute(GSXML.NAME_ATT, INDEX_FILE_STEM);
719
720 Element search_service_elem = doc.createElement(GSXML.SERVICE_CLASS_ELEM);
721 Element retrieve_service_elem = doc.createElement(GSXML.SERVICE_CLASS_ELEM);
722 service_rack_list.appendChild(search_service_elem);
723 service_rack_list.appendChild(retrieve_service_elem);
724
725 search_service_elem.setAttribute(GSXML.NAME_ATT, "GS3MGSearch");
726 search_service_elem.appendChild(index_list);
727 search_service_elem.appendChild(default_index);
728 search_service_elem.appendChild(base_index_name);
729 search_service_elem.appendChild(index_stem);
730
731 retrieve_service_elem.setAttribute(GSXML.NAME_ATT, "GS3MGRetrieve");
732 retrieve_service_elem.appendChild(default_index.cloneNode(true));
733 retrieve_service_elem.appendChild(base_index_name.cloneNode(true));
734 retrieve_service_elem.appendChild(index_stem.cloneNode(true));
735
736 return true;
737 }
738
739}
740
Note: See TracBrowser for help on using the repository browser.