source: trunk/gsdl3/src/java/org/greenstone/gsdl3/gs3build/indexers/MGIndexer.java@ 6898

Last change on this file since 6898 was 6898, checked in by kjdon, 20 years ago

added in a lot more checks for failed build commands, now uses teh index name as the directory name, remembers which indexes were successfully built, and creates some service descriptions for the buildconfig file

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