====== EL SISTEMA DE EJECUCIÓN DE GREENSTONE ====== En este capítulo se explica el sistema de ejecución de Greenstone a fin de que usted pueda comprender, aumentar y ampliar sus posibilidades. El programa está escrito en C++ y hace un uso importante de la “herencia virtual”. Si no conoce bien este lenguaje, es preferible que lo aprenda antes de seguir adelante. Deitel y Deitel (1994) propone un manual de introducción general, pero Stroustroup (1997) es la referencia de mayor autoridad. Empezaremos explicando la filosofía conceptual que sustenta el sistema de ejecución, ya que influye considerablemente en su implementación. Luego pasaremos a examinar  detalladamente la implementación, que constituye la parte principal de este capítulo. La versión de Greenstone que se expone aquí es la versión CGI (Biblioteca Web para los usuarios de Windows). La versión Biblioteca Local de Windows utiliza el mismo código fuente pero tiene un servidor Web incorporado como interfaz de usuario. Además, la Biblioteca Local es un proceso de tipo persistente. ===== Estructura de los procesos ===== {{..:images:dev_fig_20.png?483x468&direct}} En la Figura aparecen varios usuarios, representados por computadoras en la parte superior del diagrama, que acceden a tres colecciones de Greenstone. Antes de ser puestas en línea, estas colecciones pasan por los procesos de importación y creación expuestos en los capítulos anteriores. En primer lugar, los documentos, que aparecen en la parte inferior de la figura, se importan en el Formato de Archivo  Greenstone, que es un formato XML. A continuación, los archivos en el Formato de Archivo Greenstone se incorporan a varios índices de búsqueda y a una base de datos de informaciones de la colección que incluye las estructuras jerárquicas necesarias para las operaciones de consulta. Una vez hecho esto, la colección está lista para ser puesta en línea y responder a las solicitudes de información. Los dos componentes fundamentales en la concepción del sistema de ejecución son los “recepcionistas” y los “servidores de colección”. Desde el punto de vista del usuario, un recepcionista es el punto de contacto con la biblioteca digital. Recibe los datos proporcionados por los usuarios mediante el teclado y los clics del ratón, los analiza y luego transmite una solicitud al correspondiente servidor (o servidores) de la colección. Este último localiza la información solicitada y la envía al recepcionista para que la presente al usuario. Los servidores de colección actúan como un mecanismo abstracto que administra el contenido de la colección, mientras que los recepcionistas se encargan de la interfaz de usuario. {{..:images:dev_fig_21.png?483x468&direct}} Como se muestra en la Figura , los recepcionistas se comunican con los servidores de colección mediante un protocolo definido. La implementación de este protocolo depende de la configuración de la computadora en la que se ejecuta el sistema de biblioteca digital. El caso más común, y el más sencillo, es cuando hay un recepcionista y un servidor de colección y ambos operan en la misma computadora. Eso es lo que se obtiene con la instalación por defecto de Greenstone. En este caso, los dos procesos se combinan para formar un solo ejecutable (denominado //library//) y, por consiguiente, la utilización del protocolo se reduce a llamadas de función. A esto lo llamamos //null protocol// (protocolo nulo) y es la base del sistema estándar de biblioteca digital Greenstone en su estado original. Esta configuración simplificada aparece en la Figura , en la que el recepcionista, el protocolo y el servidor de colección están unidos en una sola entidad, el programa //library// (biblioteca). La finalidad de este capítulo es explicar cómo funciona. Normalmente, un “servidor” es un proceso persistente que, una vez iniciado, funciona indefinidamente y responde a cualquier solicitud que le llegue. No obstante, a pesar de su nombre, el servidor de colección en la configuración de protocolo nulo no es un servidor en el sentido estricto. De hecho, cada vez que se solicita una página Web de Greenstone, se arranca el programa //library// (mediante el mecanismo CGI), éste responde a la solicitud y luego se cierra. Lo llamamos “servidor” porque también está diseñado para funcionar con una configuración más general como la de la Figura . Por más sorprendente que parezca, este ciclo de arranque, tratamiento y cierre no es tan lento como se podría imaginar y el resultado es un servicio perfectamente utilizable. Sin embargo, el sistema es poco eficiente. Existe un mecanismo llamado Fast-CGI (// www.fastcgi.com //) que ofrece una buena solución de compromiso. Gracias a su utilización, el programa //library// puede permanecer en memoria después de la primera ejecución y recibir las subsiguientes series de argumentos CGI, con lo que se evita repetir la inicialización y su función se asemeja más a la de un servidor convencional. La utilización de Fast-CGI es una opción de Greenstone y se activa volviendo a compilar el código fuente con las bibliotecas adecuadas. Como alternativa al protocolo nulo, hemos elaborado también el protocolo Greenstone utilizando el famoso sistema CORBA (Slama //et al.//, 1999), que utiliza un paradigma unificado orientado a objetos, para permitir a distintos procesos ejecutándose en distintas plataformas informáticas e implementados en diferentes lenguajes de programación, acceder a la misma serie de objetos distribuidos por Internet (o cualquier otra red). Así pues, una situación como la de la Figura , en la que todos los recepcionistas y los servidores de colección operan en diferentes computadoras, es perfectamente viable. {{..:images:dev_fig_22.png?397x347&direct}} Ello permite la instalación de interfaces mucho más complejas para distribuir exactamente las mismas colecciones de biblioteca digital. A modo de ejemplo, en la Figura se muestra una interfaz gráfica de consultas, basada en los diagramas de Venn, que permite a los usuarios manipular directamente las consultas booleanas. Escrita en Java, la interfaz funciona localmente en la computadora del usuario. Con el sistema CORBA, se puede acceder a un servidor remoto de colección de Greenstone, escrito en C++. No nos extenderemos más sobre este tema en el presente manual, ya que el protocolo distribuido sigue siendo objeto de perfeccionamiento y mejoras para su utilización (para obtener más información, véase Bainbridge //et al//., 2001). ===== Marco conceptual ===== {{..:images:dev_fig_23.png?604x457&direct}} En la Figura se muestra la página “Acerca de esta colección” de una colección Greenstone particular (la colección del Proyecto Gutenberg). Fíjese en la dirección URL que aparece en la parte superior: esta página se obtiene activando el programa CGI //library//, que es el ejecutable anteriormente mencionado en que el recepcionista y el servidor de colección están conectados mediante el protocolo nulo. Los argumentos del programa //library// son //c=gberg//, //a=p//, //p=about// y //l=es//. Pueden interpretarse del modo siguiente: > Para acceder a la colección del Proyecto Gutenberg (c=gberg), la acción consiste en crear una página (a=p) y la página que hay que generar se llama “about” (p=about), en español (l=es). {{..:images:dev_fig_24_resized.png?287x467&direct}} La Figura ilustra las principales partes del sistema de ejecución de Greenstone. En la parte superior, el recepcionista inicializa primero sus componentes, luego analiza los argumentos CGI para decidir qué acción activar. Al ejecutar la acción (que incluye nuevos tratamientos de los argumentos CGI), el programa utiliza el protocolo para acceder al contenido de la colección. Se utiliza la respuesta para generar una página Web, con la asistencia del componente de formato y del lenguaje de macros. El lenguaje de macros, que ya examinamos en la Sección [[#controlling_the_greenstone_user_interface|controlling_the_greenstone_user_interface]], se utiliza para conferir al sistema de Biblioteca Digital Greenstone un estilo coherente y crear interfaces en diferentes lenguas. La interacción con la biblioteca genera la estructura básica de las páginas Web y las macros del directorio //GSDLHOME/macros// aportan el contenido. El objeto Lenguaje de macros representado en la Figura se encarga de leer estos archivos y de memorizar el resultado del análisis. Cualquier acción puede utilizar este objeto para ampliar una macro. Puede incluso crear nuevas definiciones de macro e invalidar las anteriores, aportando así una dimensión dinámica al uso de las macros. La representación de la página “Acerca de esta colección” (Figura ) se conoce antes de la ejecución y está codificada en el archivo de macros //about.dm//. Los encabezados, los pies de página y la imagen de fondo ni siquiera se mencionan porque se encuentran en el paquete de macros //Global//. Sin embargo, el texto específico de “acerca de” correspondiente a una colección particular no se conoce de antemano, sino que se guarda en la base de datos de informaciones de la colección durante el proceso de creación. Esta información se recupera mediante el protocolo y se guarda como macro _ //collectionextra_// en el paquete de macros //Global//. Obsérvese que este nombre de macro es prácticamente el mismo que se empleó para expresar esta información en el archivo de configuración de colección mencionado en la Sección [[#import_and_build_processes|import_and_build_processes]]. Para generar el contenido de la página, se amplía la macro _ //content// _ en el paquete //about// (que se muestra en la Figura ). Esta operación amplía a su vez _ //textabout// _, que accede a la macro _ //collectionextra// _ que acababa de colocarse allí de manera dinámica. Otro componente importante es el objeto Formato. Las instrucciones de formato en el archivo de configuración de colección inciden en la presentación de determinados elementos de información, como se explica en la Sección [[#formatting_greenstone_output|formatting_greenstone_output]]. Su gestión corre a cargo del objeto Formato representado en la Figura . La principal tarea de este objeto es analizar y evaluar instrucciones como las cadenas de formato de la Figura . Tal como vimos en la Sección [[#formatting_greenstone_output|formatting_greenstone_output]], éstas pueden comprender referencias a metadatos entre corchetes (por ejemplo, //[Title]//), que han de recuperarse del servidor de colección. Se producen interacciones entre el objeto Formato y el objeto Lenguaje de macros, ya que las instrucciones de formato pueden incluir macros, que cuando se amplían incluyen metadatos, y cuando éstos se amplían a su vez incluyen macros, y así sucesivamente. El servidor de colección, visible en la parte inferior de la Figura , experimenta también un proceso de inicialización, en el que establece objetos Filtro y de fuente para responder a las solicitudes que recibe del protocolo, y un objeto Búsqueda para prestar asistencia en esta tarea. En última instancia, los objetos acceden a los índices y las bases de datos de informaciones de colección, ambos elaborados durante la creación de la colección. Sin contar las líneas en blanco, el recepcionista contiene 15.000 líneas de código. El servidor de colección sólo contiene 5.000 líneas, el 75% de las cuales están ocupadas por archivos de encabezado. El servidor de colección es más compacto porque la recuperación de los datos se lleva a cabo mediante dos programas precompilados. Se utiliza MG, un sistema de recuperación de texto completo, para las búsquedas y GDBM, un sistema de gestión de bases de datos, para la gestión de la base de datos de informaciones de colección. Para facilitar la extensibilidad y la flexibilidad, Greenstone utiliza considerablemente el método de herencia, en particular, en los objetos Acción, Filtro, Fuente y Búsqueda. Esto significa que, para manejar una simple biblioteca digital que sólo contenga colecciones de textos, usted tiene que aprender un poco más para programar el sistema. No obstante, esto también significa que MG y GDBM podrían sustituirse fácilmente si ello fuera necesario. Además, la arquitectura del programa es lo suficientemente rica para admitir todas las posibilidades multimedia, como el control de la interfaz por medio de sistemas de reconocimiento de voz o  consultas en forma de gráfica. ===== Ajuste del marco conceptual ===== En las Secciones [[#collection_server|collection_server]] y [[#receptionist|receptionist]] se explica más detalladamente el funcionamiento del servidor de colección y del recepcionista, se amplía la información sobre los módulos de la Figura y se explica su implementación. Es conveniente, en primer lugar, ilustrar con ejemplos la interacción de un usuario con el programa Greenstone y luego explicar lo que sucede “entre bastidores”. De momento, supondremos que todos los objetos están inicializados correctamente. La inicialización es un procedimiento bastante complejo que reexaminaremos en la Sección [[#initialisation|initialisation]]. ==== Efectuar una búsqueda ==== {{..:images:dev_fig_25.png?604x375&direct}} Cuando un usuario introduce una consulta y pulsa //Iniciar la búsqueda// en la página de búsqueda, se activa una acción Greenstone que culmina generando una nueva página HTML mediante el lenguaje de macros. En la Figura se muestra el resultado de la búsqueda del nombre //Darcy// en la colección del Proyecto Gutenberg. La instrucción //a=q// se encuentra oculta en el HTML de la página original de búsqueda. Cuando se pulsa el botón de búsqueda se activa esta instrucción y se ejecuta la nueva acción //queryaction//. La ejecución de //queryaction// consiste en una llamada al objeto Filtro de la colección designada ( //c=gberg//) a través del protocolo. Los filtros desempeñan una función básica importante en los servidores de colección. Adaptados a las actividades de búsqueda y consulta, los filtros permiten seleccionar un subconjunto de información en una colección. En este caso, //queryaction// inicia una solicitud de filtro: * estableciendo que el tipo de solicitud de filtro es //QueryFilter// (los distintos tipos de filtro se explican en la Sección [[#collection_server|collection_server]]); * memorizando las preferencias de búsqueda del usuario – reconocimiento de mayúsculas y minúsculas, truncamiento, etc.– en la solicitud de filtro; * activando la función //filter()// mediante el protocolo nulo. Las llamadas al protocolo son síncronicas. El recepcionista queda efectivamente bloqueado hasta que el servidor de colección trata la solicitud de filtro y se envían los datos generados. Cuando se hace una llamada de protocolo de tipo //QueryFilter//, el objeto de filtro (véase la Figura ) descodifica las opciones y llama al objeto Búsqueda, que utiliza MG para efectuar la búsqueda propiamente dicha. La función del objeto Búsqueda es proporcionar una interfaz abstracta de programa que permita la búsqueda, independientemente de la herramienta de búsqueda subyacente que se utilice. El formato utilizado para enviar los resultados recurre también a la abstracción, lo cual obliga el objeto Búsqueda a traducir los datos generados por la herramienta de búsqueda a una forma normalizada. Una vez enviados los resultados de búsqueda al recepcionista, se procede a formatearlos para su presentación utilizando los objetos Formato y Lenguaje de macros. Como se muestra en la Figura , esta operación supone: la creación del encabezado, el pie de página, la barra de desplazamiento y la imagen de fondo normalizados de Greenstone; la repetición de la parte principal de la página de consulta justo debajo de la barra de desplazamiento; y la presentación de un icono de libro, así como el título y el autor para cada uno de los resultados de la búsqueda. El formato de esta última parte depende de la instrucción //format SearchVList// que se encuentra en el archivo de configuración de la colección. Para poder mostrar los metadatos de título y autor, éstos han de recuperarse antes del servidor de colección, lo que requiere nuevas llamadas al protocolo, pero esta vez utilizando //BrowseFilter// (filtro de consulta). ==== Recuperación de un documento ==== Siguiendo con la búsqueda anterior de //Darcy//, obsérvese lo que sucede cuando visualiza un documento. En la Figura se muestra el resultado de pulsar el icono que se encuentra junto a //The Golf Course Mystery// en la Figura . {{..:images:dev_fig_26.png?465x461&direct}} El texto de origen de la colección Gutenberg consiste en un extenso archivo por cada libro. Durante la creación de la colección, dichos archivos se dividen en páginas separadas cada 200 líneas aproximadamente, y las informaciones importantes de cada página se guardan en los índices y en la base de datos de informaciones de la colección. En la parte superior de la Figura se indica que este libro tiene 104 páginas electrónicas, y justo debajo aparece el principio de la primera página: el nombre de la persona que introdujo el texto, el título, el autor y el principio de un índice (dicho índice forma parte del texto de origen del Proyecto Gutenberg y no ha sido generado por Greenstone). En la parte superior izquierda se ven los botones que controlan la presentación del documento: una sola página o la totalidad del documento, si se resalta o no el término de la consulta, y si se muestra o no el libro en su propia ventana, separado de la actividad principal de búsqueda y consulta. En la parte superior derecha hay un asistente de consulta que proporciona acceso directo a cualquier página del libro: basta con escribir el número de la página y pulsar el botón “ir a la página”. Asimismo, se pueden consultar las páginas anteriores y posteriores haciendo clic en los iconos de flecha situados de cada lado del dispositivo de selección de página. La operación de recuperación de documentos, //documentaction//, se especifica mediante la configuración //a=d// y requiere varios argumentos suplementarios. Lo más importante es el documento por recuperar: éste se define mediante la variable //d//. En la Figura su valor es //d=HASH51e598821ed6cbbdf0942b.1// para recuperar la primera página del documento cuyo identificador es //HASH51e598821ed6cbbdf0942b//, y que en términos más convencionales se conoce como //The Golf Course Mystery//. Hay otras variables mediante las cuales se determina si el término de búsqueda ha de resaltarse o no ( //hl//) y qué página del libro se debe mostrar ( //gt//). Estas variables complementan las opciones propuestas por los botones de la página Web representados en la Figura y antes mencionados. Se utilizan los valores por defecto cuando se omite cualquiera de estas variables. Esta acción sigue un procedimiento similar a //queryaction// : examen de los argumentos CGI, acceso al servidor de colección mediante el protocolo y utilización del resultado para generar una página Web. Las opciones relativas al documento se descodifican a partir de los argumentos CGI y se guardan en el objeto para operaciones ulteriores. Para recuperar el documento desde el servidor de colección, sólo se necesita el identificador de documento para efectuar la llamada de protocolo //get_document()//. Una vez enviado el documento, queda por hacer un trabajo considerable de formateo. Para ello, el código de //documentaction// accede a los argumentos almacenados y utiliza los objetos Formato y Lenguaje de macros. ==== Consulta de un clasificador jerárquico ==== En la Figura se muestra un ejemplo de consulta, en que el usuario ha elegido //títulos a-z// y ha accedido al hipervínculo de la letra //K//. Esta operación se basa también en //documentaction,// función proporcionada por la especificación del argumento CGI //a=d//, como en el caso anterior. Sin embargo, la diferencia es que antes se incluyó la variable //d// y esta vez no. En su lugar, el nodo de la jerarquía de clasificación por visualizar se especifica con la variable //cl//, que en este caso representa los títulos agrupados bajo la letra //K//. Esta lista se constituyó durante el proceso de creación y está guardada en la base de datos de informaciones de la colección. {{..:images:dev_fig_27.png?605x519&direct}} Los registros que representan nodos de clasificador en la base de datos utilizan el prefijo //CL//, seguido de números separados por puntos (.) para indicar dónde se encuentran dentro de la estructura de subdivisiones. Si se ignora el botón de búsqueda (visible en el extremo izquierdo de la barra de desplazamiento), los clasificadores se numeran secuencialmente por orden ascendente, de izquierda a derecha, empezando por 1. Así pues, el nodo de clasificador de primer nivel para los títulos en nuestro ejemplo es //CL1//, y la página buscada se obtiene indicando //cl=CL1.11//, como puede verse en la dirección URL que aparece en la parte superior de la Figura . Para tratar una solicitud de documento de tipo //cl//, se utiliza el objeto Filtro para recuperar el nodo a través del protocolo. Según los datos enviados, se efectúan ulteriores llamadas de protocolo para recuperar los metadatos de los documentos. En este caso, se obtienen los títulos de los libros. Sin embargo, si se tratara de un nodo interno cuyos elementos secundarios fueran a su vez nodos, se recuperarían también los títulos de los nodos secundarios. Desde el punto de vista de la codificación es lo mismo y de ello se encarga el mismo mecanismo. Por último, toda la información recuperada se interconecta mediante el lenguaje de macros a fin de crear la página Web que se muestra en la Figura . ==== Generación de la página principal ==== {{..:images:dev_fig_28.png?604x571&direct}} Como último ejemplo, examinaremos el caso de la generación de la página principal de Greenstone. En la Figura se muestra –en el caso de la instalación por defecto de Greenstone– su página principal tras la instalación de algunas colecciones de prueba. La dirección URL, que puede verse en la parte superior de la pantalla, incluye los argumentos //a=p// y //p=home//. Así pues, al igual que en el caso de la página “Acerca de esta colección”, ésta se genera mediante una //pageaction// (a=p), pero esta vez la página creada es //home// ( //p=home//). Por consiguiente, el lenguaje de macros accede al contenido del archivo //home.dm//. En este caso no es necesario especificar una colección (mediante la variable //c//). La finalidad de la página principal es mostrar las colecciones disponibles. Cuando el usuario hace clic en un icono, aparece la página “Acerca de esta colección” de la colección correspondiente. Cada vez que se carga la página, se genera dinámicamente el menú de las colecciones basándose en las colecciones que se encuentran en el sistema de archivos en ese momento. Cuando una nueva colección se pone en línea, aparece automáticamente en la página principal cuando ésta se vuelve a cargar, siempre que se haya especificado que la colección es “pública”. Para ello, el recepcionista utiliza, desde luego, el protocolo. Como parte de su función de evaluación de los argumentos CGI, la acción //pageaction// está programada para detectar el caso particular //p=home//. A continuación, la acción utiliza la llamada de protocolo //get_collection_list()// para establecer el conjunto de colecciones que se encuentran en línea y activa //get_collectinfo()// a fin de obtener información sobre cada una de ellas. Dicha información indica si la colección puede consultarse públicamente, cuál es la dirección URL del icono de la colección (si es que existe) y el nombre completo de la colección. Estos datos se utilizan para generar una entrada apropiada de la colección en la página principal. ===== El código fuente ===== |< - 132 397 >| | //setpasswd/// | Ayuda para las contraseñas bajo Windows. | | //getpw/// | Ayuda para las contraseñas bajo Unix. | | //txt2db/// | Convierte un formato de texto ASCII de tipo XML en formato de base de datos de GNU. | | //db2txt/// | Convierte el formato de base de datos de GNU en formato de texto ASCII de tipo XML. | | //phind/// | Herramienta de consulta jerárquica en los grupos de palabras. | | //hashfile/// | Calcula un identificador de documento único, basándose en el contenido del archivo. | | //mgpp/// | Versión reescrita y actualizada del paquete //Managing Gigabytes// en C++ | | //w32server/// | Servidor de Biblioteca Local para Windows. | | //checkis/// | Ayuda específica para la instalación de Greenstone bajo Windows. | El código fuente del sistema de ejecución se encuentra en //GSDLHOME/src//. Ocupa dos subdirectorios, //recpt// para el código del recepcionista y //colservr// para el del servidor de colección. Greenstone funciona bajo Windows a partir de la versión Windows 3.1, lo cual impone desafortunadamente un máximo de ocho caracteres en los nombres de archivos y directorios. Esto explica la utilización de abreviaciones crípticas como //recpt// y //colservr//. Los demás subdirectorios incluyen programas independientes que se utilizan principalmente para reforzar el proceso de creación (véase el Cuadro ). Otro directorio, //GSDLHOME/lib//, incluye objetos de nivel inferior que el recepcionista y el servidor de colección utilizan. En la Sección [[#common_greenstone_types|common_greenstone_types]] se explica este código. Greenstone utiliza considerablemente la Biblioteca Estándar de Plantillas (STL), una biblioteca C++ de amplia difusión concebida por Silicon Graphics ( // www.sgi.com //), y que es el resultado de numerosos años de investigación y desarrollo. Como todas las bibliotecas de programación requiere un tiempo de aprendizaje. En el Apéndice A se presenta un breve resumen de las principales porciones que se utilizan en el código de Greenstone. Para una descripción más completa, consulte el manual oficial de referencia de STL disponible en la dirección Internet // www.sgi.com //, o alguno de los numerosos manuales sobre STL, como por ejemplo el de Josuttis (1999). ===== Tipos básicos de Greenstone ===== Los elementos definidos en el directorio //GSDLHOME/lib// son objetos Greenstone de nivel inferior, creados por encima de STL, y que se utilizan en todo el código fuente. En primer lugar, describimos con detalles //text_t//, un objeto utilizado para representar texto Unicode y luego resumimos la finalidad de cada uno de los archivos de biblioteca. ==== El objeto text_t ==== Greenstone funciona con múltiples idiomas, tanto en lo que se refiere al contenido de una colección como a la interfaz de usuario. Para ello, se utiliza Unicode a todo lo largo del código fuente. El objeto subyacente que realiza una cadena Unicode es //text_t//. typedef vector usvector; class text_t { protected: usvector text; unsigned short encoding; // 0 = unicode, 1 = other public:   // constructors   text_t ();   text_t (int i);   text_t (char *s); // assumed to be a normal c string   void setencoding (unsigned short theencoding);   unsigned short getencoding ();   // STL container support   iterator begin ();   iterator end ();   void erase(iterator pos);   void push_back(unsigned short c);   void pop_back();   void reserve (size_type n);   bool empty () const {return text.empty();}   size_type size() const {return text.size();}   // added functionality   void clear ();   void append (const text_t &t);   // support for integers   void appendint (int i);   void setint (int i);   int getint () const;   // support for arrays of chars   void appendcarr (char *s, size_type len);   void setcarr (char *s, size_type len); }; Unicode necesita dos bytes para almacenar cada carácter. En la Figura se muestran las características principales de la interfaz para programas de aplicación (API, esto es, //Application Program Interface//) de //text_t//. El requisito de los dos bytes se cumple utilizando el tipo estándar C++ //short//, que se define como un número entero de dos bytes. El principal tipo de datos del objeto //text_t// es una matriz dinámica de //unsigned////shorts// elaborada mediante la declaración STL //vector// y que recibe el nombre abreviado de //usvector//. Las funciones de creación (líneas 10-12) admiten explícitamente tres formas de inicialización: creación sin parámetros, que genera una cadena Unicode vacía; creación con un parámetro entero, que genera una versión de texto Unicode con el valor numérico proporcionado; y creación con un parámetro //char*//, que trata el argumento como una cadena C++ terminada en cero y genera una versión Unicode del mismo. A continuación, la mayor parte del código (líneas 17-28) se ocupa de mantener un contenedor de tipo vector STL: //begin()//, //end()//, //push_back()//, //empty()//, etc. Permite asimismo borrar y concatenar cadenas, así como convertir un número entero en una cadena de texto Unicode y remitir el correspondiente valor entero a un texto que representa un número. class text_t{       // ...       public:       text_t &operator=(const text_t &x);       text_t &operator+=(const text_t &t);       reference operator[](size_type n);       text_t &operator=(int i);       text_t &operator+= (int i);       text_t &operator= (char *s);       text_t &operator+= (char *s);       friend inline bool operator!=(const text_t& x, const text_t& y);       friend inline bool operator==(const text_t& x, const text_t& y);       friend inline bool operator< (const text_t& x, const text_t& y);       friend inline bool operator> (const text_t& x, const text_t& y);       friend inline bool operator>=(const text_t& x, const text_t& y);       friend inline bool operator<=(const text_t& x, const text_t& y);       // ... }; Existen numerosos operadores sobrecargados que no aparecen en la Figura . La Figura nos da una idea general de las operaciones posibles. La línea 4 permite asignar un objeto //text_t// a otro, y la línea 5 sobrecarga el operador += para poder concatenar de modo más natural un objeto //text_t// al final de otro. La línea 6 permite asimismo tener acceso a un carácter Unicode particular (representado como un //short//) utilizando subíndices de matriz [ ]. Se proporcionan asimismo operadores de asignación y concatenación para los números enteros y las cadenas C++. Las líneas 12-18 suministran operadores booleanos para comparar dos objetos //text_t// : igual a, diferente de, precede alfabéticamente, etc. Existen asimismo funciones miembro que toman argumentos de tipo //const// en lugar de argumentos no //-const//, pero no se muestran aquí. Dicha repetición es habitual en los objetos C++: aumenta el volumen de la API pero sin incrementarla conceptualmente. En realidad, muchas de estas funciones se implementan como instrucciones individuales de una sola línea. Para mayor información, consulte el archivo fuente //GSDLHOME/lib/text_t.h//. ==== El código de biblioteca Greenstone ==== Los archivos de encabezados situados en el directorio //GSDLHOME/lib// comprenden una mezcla de funciones y objetos que contribuyen útilmente al sistema de ejecución de Greenstone. Cuando lo importante es la eficiencia, las funciones y las funciones miembro se especifican //inline//. En la mayoría de los casos, los detalles de implementación se incluyen en el archivo //.cpp// correspondiente al archivo de encabezados. |< - 100 450 >| | **cfgread.h** | Funciones de lectura y escritura de archivos de configuración. Por ejemplo, la función //read_cfg_line()// (leer archivo de configuración) toma como argumentos el flujo de entrada y la matriz //text_tarray// (abreviación de //vector//) para rellenar con los datos leídos. | | **display.h** | Objeto complejo utilizado por el recepcionista para definir, almacenar y ampliar macros, y para manejar tipos. En la Sección [[#receptionist|receptionist]] se proporciona mayor información al respecto. | | **fileutil.h** | Auxiliar para funciones de gestión de archivos independientes del sistema operativo. Por ejemplo, //filename_cat()// admite hasta seis argumentos //text_t// y devuelve un //text_t// que es el resultado de concatenar los elementos utilizando el separador de directorio adecuado para el sistema operativo en cuestión. | | **gsdlconf.h** | Funciones específicas del sistema que contestan a preguntas como: ¿el sistema operativo que se está utilizando para la compilación necesita acceder al archivo  //strings.h// así como al archivo //string.h// ? ¿Se han definido correctamente todos los valores apropiados para el bloqueo de archivos? | | **gsdltimes.h** | Función de apoyo para fechas y horas. Por ejemplo, //time2text()// toma la hora de la computadora, expresada como el número de segundos transcurridos desde el 1º de enero de 1970, y la convierte en el formato AAAA/MM/DD hh:mm:ss, que devuelve como tipo //text_t//. | | **gsdltools.h** | Apoyo variado al sistema de ejecución de Greenstone: determina si se trata de //littleEndian// o //bigEndian// ; comprueba si Perl está disponible; ejecuta un comando del sistema (con “campaneos y silbidos”, esto es, con ciertas cualidades avanzadas y exageradas del programa); y elude los caracteres especiales de macro en una cadena //text_t//. | | **gsdlunicode.h** | Serie de objetos heredados que admiten tratar las cadenas //text_t// de Unicode a través de flujos de entrada y salida, como las conversiones de Unicode en UTF-8 y viceversa; y la supresión de los espacios de ancho cero. Administra asimismo los archivos de correspondencia mediante el objeto //mapconvert//, y carga los cuadros de correspondencia en el directorio //GSDLHOME/mappings//. | | **text_t.h** | Principalmente el objeto de texto de Unicode antes expuesto. Proporciona asimismo dos clases de conversión de flujos: //inconvertclass// y //outconvertclass//. Son las clases de base utilizadas en el archivo //gsdlunicode.h//. | ===== El servidor de colecciones ===== Ahora pasaremos a explicar sistemáticamente todos los objetos del marco conceptual de la Figura . Empezaremos por la parte inferior del diagrama, que constituye además la base del sistema, con los objetos Búsqueda (//Search//), Fuente ( //Source//) y Filtro ( //Filter//), y recorreremos las capas del protocolo hasta llegar a los componentes centrales del Recepcionista, Acciones, Formato y Lenguaje de macros. Luego nos centraremos en la inicialización de los objetos, ya que es más fácil entender este proceso una vez que se conoce la función de los distintos objetos. La mayoría de las clases fundamentales del marco conceptual se expresan mediante la herencia virtual para facilitar la extensibilidad. Mediante la herencia virtual, los objetos heredados se pueden transferir como su clase base, pero cuando se activa una función miembro, la versión definida en el objeto heredado es la que se ejecuta. Como el código fuente de Greenstone utiliza la clase base en todas partes, excepto donde se crean los objetos, esto significa que se pueden insertar fácilmente diferentes implementaciones, utilizando tal vez tecnologías subyacentes radicalmente diferentes. Por ejemplo, supongamos que una clase de base denominada //BaseCalc// se encarga de la aritmética elemental: suma, resta, multiplicación y división. Si todas sus funciones se declaran virtuales, y todos los argumentos y tipos devueltos se declaran como cadenas, podemos implementar fácilmente las versiones heredadas del objeto. Una de ellas, llamada //FixedPrecisionCalc//, podría utilizar las funciones de la biblioteca C para las conversiones entre cadenas y números enteros e implementar los cálculos con los operadores aritméticos habituales: +, -, *,  y /. La otra, llamada //InfinitePrecisionCalc//, podría acceder a los argumentos de cadena carácter por carácter, efectuando operaciones aritméticas que en principio son de una precisión infinita. Escribiendo un programa principal que utiliza //BaseCalc// por doquier, la implementación puede pasar de la precisión finita a la precisión infinita modificando una sola línea: el lugar donde se crea el objeto de cálculo. ==== El objeto Búsqueda (Search) ==== class searchclass { public:   searchclass ();   virtual ~searchclass ();   // the index directory must be set before any searching   // is done   virtual void setcollectdir (const text_t &thecollectdir);   // the search results are returned in queryresults   // search returns ’true’ if it was able to do a search   virtual bool search(const queryparamclass &queryparams,                    queryresultsclass &queryresults)=0;   // the document text for ’docnum’ is placed in ’output’   // docTargetDocument returns ’true’ if it was able to   // try to get a document   // collection is needed to see if an index from the   // collection is loaded. If no index has been loaded   // defaultindex is needed to load one   virtual bool docTargetDocument(const text_t &defaultindex,                         const text_t &defaultsubcollection,                         const text_t &defaultlanguage,                         const text_t &collection,                         int docnum,                         text_t &output)=0; protected:   querycache *cache;   text_t collectdir; // the collection directory }; En la Figura se muestra el API de la clase de base para el objeto de búsqueda de la Figura . Define dos funciones miembro virtuales: //search()// y //docTargetDocument()//. Tal como indica el //=0// que sigue la declaración de argumento, se trata de funciones puras, lo que significa que una clase que hereda este objeto debe implementar ambas funciones (de otro modo el compilador se quejará). La clase incluye asimismo dos campos de datos protegidos: //collectdir// y //cache//. Cuando se instancia un objeto de búsqueda para una colección particular, se utiliza el campo //collectdir// para determinar dónde está guardada esa colección (y, lo que es más importante, sus archivos de índice) en el sistema de archivos. El campo //cache// retiene el resultado de una consulta. Se utiliza para acelerar el tratamiento de consultas ulteriores que repiten la primera (y sus parámetros). Puede parecer improbable que se efectúen consultas idénticas, pero de hecho esto ocurre con bastante frecuencia. El protocolo de Greenstone es sin estado. A fin de generar una página de resultados como la de la Figura , pero con los resultados 11-20 de la misma consulta, se transmite de nuevo la búsqueda especificando, en este caso, que se presente la serie de documentos 11-20. El campo //cache// acelera esta operación, puesto que se comprueba que la búsqueda ya ha sido ejecutada y los resultados se sacan directamente de este campo. Ambos campos de datos son aplicables a todos los objetos heredados que implementan un mecanismo de búsqueda. Por ello aparecen en la clase de base y se declaran dentro de una sección protegida de la clase, a fin de que las clases heredadas puedan acceder directamente a ellos. ==== Búsqueda y recuperación con MG ==== Greenstone utiliza MG (abreviación de //Managing Gigabytes// ; véase Witten //et al//., 1999) para indizar y recuperar documentos, y su código fuente se incluye en el directorio //GSDLHOME/packages//. MG emplea técnicas de compresión para optimizar la utilización del espacio de disco sin alterar la rapidez de ejecución. En el caso de una colección de documentos en inglés, el texto comprimido y los índices suelen ocupar un tercio del espacio que necesitaría el texto original sin comprimir. La búsqueda y recuperación suelen ser más rápidas que las operaciones equivalentes en la versión sin comprimir porque requieren menos operaciones en el disco duro. enum result_kinds {   result_docs,      // Return the documents found in last search   result_docnums,   // Return document id numbers and weights   result_termfreqs, // Return terms and frequencies   result_terms      // Return matching query terms }; int mgq_ask(char *line); int mgq_results(enum result_kinds kind, int skip, int howmany,                 int (*sender)(char *, int, int, float, void *),                 void *ptr); int mgq_numdocs(void); int mgq_numterms(void); int mgq_equivterms              (unsigned char *wordstem,               int (*sender)(char *, int, int, float, void *),               void *ptr); int mgq_docsretrieved (int *total_retrieved, int *is_approx); int mgq_getmaxstemlen (); void mgq_stemword (unsigned char *word); MG se utiliza por lo general de manera interactiva tecleando las instrucciones desde la línea de comandos, y un modo de implementar //mgsearchclass// sería activando la llamada //system()// de la biblioteca C dentro del objeto para ejecutar los comandos MG apropiados. Sin embargo, un método más eficaz consiste en intervenir directamente en el código de MG utilizando las llamadas de función. Aunque esto requiere un mayor conocimiento del código de MG, una gran parte de la complejidad puede ocultarse tras una nueva API que pasará a ser el punto de contacto para el objeto //mgsearchclass//. Ésta es la función del archivo //colserver/mgq.c//, cuya API se muestra en la Figura . Para proporcionar parámetros a MG, se usa //mgq_ask()//, que admite opciones de texto en un formato idéntico al que se usa en la línea de comandos, como por ejemplo: mgq_ask(“.set casefold off”); Se utiliza asimismo para activar una consulta. Se accede a los resultados mediante la función //mgq_results//, que adopta como cuarto parámetro un puntero dirigido hacia una función. Ello proporciona un modo flexible de convertir la información devuelta en estructuras de datos de MG en el formato que requiere //mgsearchclass//. Las operaciones como //mgq_numdocs()//, //mgq_numterms()// y //mgq_docsretrieved()// también devuelven información, pero prescritas de manera más precisa. Las dos últimas contribuyen a la función de truncamiento. ==== El objeto Fuente (Source) ==== class sourceclass { public:   sourceclass ();   virtual ~sourceclass ();   // configure should be called once for each configuration line   virtual void configure (const text_t &key,                           const text_tarray &cfgline);   // init should be called after all the configuration is done but   // before any other methods are called   virtual bool init (ostream &logout);   // translate_OID translates OIDs using ".pr", ."fc" etc.   virtual bool translate_OID (const text_t &OIDin, text_t &OIDout,                               comerror_t &err, ostream &logout);   // get_metadata fills out the metadata if possible, if it is not   // responsible for the given OID then it returns false.   virtual bool get_metadata (const text_t &requestParams,                              const text_t &refParams,                              bool getParents,                              const text_tset &fields,                              const text_t &OID,                              MetadataInfo_tmap &metadata,                              comerror_t &err, ostream &logout);   virtual bool get_document (const text_t &OID, text_t &doc,                              comerror_t &err, ostream &logout); }; La función del objeto Fuente (//Source//) de la Figura es acceder a los metadatos y al texto del documento; la API de su clase de base se muestra en la Figura . Una función miembro se asigna a cada tarea: las funciones //get_metadata()// y //get_document()//, respectivamente. Ambas se declaran //virtual//, por lo que la versión suministrada por una implementación particular de la clase de base se activa durante la ejecución. Una versión heredada de este objeto utiliza GDBM para implementar //get_metadata()// y MG para implementar //get_document()// : a continuación explicaremos en detalle esta versión. Las otras funciones miembro que aparecen en la Figura son //configure()//, //init()// y //translate_OID()//. Las dos primeras se relacionan con el proceso de inicialización que se explica en la Sección [[#initialisation|initialisation]]. La otra, //translate_OID()//, maneja la sintaxis para expresar los identificadores de documento. En la Figura vimos que el número de una página podía anexarse a un identificador de documento para recuperar únicamente esa página. Esto era posible porque, al crearse la colección, las páginas se almacenaban como “secciones”. Si se agrega “.1” a un OID, se recupera la primera sección del documento correspondiente. Las secciones pueden subdividirse y para acceder a ellas es preciso concatenar números de secciones separados por puntos. Además de los números jerárquicos de secciones, la sintaxis del identificador de documento admite una forma de acceso relativo. En el caso de la sección en uso de un documento, se puede acceder al //primer hijo// anexando //.fc (first child)//, al //último niño// anexando //.lc (last child)//, al //padre// anexando //.pr (parent)//, al //hermano////siguiente// anexando //.ns////(next sibling)// y al //hermano anterior// anexando //.ps (previous sibling)//. La función //translate_OID// utiliza los parámetros //OIDin// y //OIDout// para retener la fuente y el resultado de la conversión. Incluye otros dos parámetros: //err// y //logout//. Éstos comunican cualquier estado de error que se pueda producir durante la operación de traducción, y determina dónde enviar la información de registro ( //logging information//). Como veremos en la Sección [[#protocol|protocol]], estos parámetros dependen estrechamente del protocolo. === Recuperación de base de datos con gdbm === GDBM es el programa de gestión de base de datos de GNU (// www.gnu.org //). Establece una estructura de registros planos de pares clave/datos, y es compatible con versiones anteriores de DBM y NDBM. Las operaciones incluyen el almacenamiento, la recuperación y la supresión de registros por clave, y un recorrido no ordenado de todas las claves. [HASH01d7b30d4827b51282919e9b] doc            0             The Winter’s Tale <Creator> William Shakespeare <archivedir>       HASH01d7/b30d4827.dir <thistype>         Invisible <childtype>        Paged <contains>         ".1;".2;".3;".4;".5;".6;".7;".8;".9;".10;".11;".12; \                  ".13;".14;".15;".16;".17;".18;".19;".20;".21;".22; \                  ".23;".24;".25;".26;".27;".28;".29;".30;".31;".32; \                  ".33;".34;".35 <docnum>           168483 ------------------------ [CL1] <doctype> classify <hastxt>           0 <childtype>        HList <Title>            Title <numleafdocs>      1818 <thistype>         Invisible <contains>         ".1;".2;".3;".4;".5;".6;".7;".8;".9;".10;".11;".12; \                  ".13;".14;".15;".16;".17;".18;".19;".20;".21;".22; \                  ".23;".24 ------------------------ [CL1.1] <doctype> classify <hastxt>           0 <childtype>        VList <Title>            A <numleafdocs>      118 <contains>         HASH0130bc5f9f90089b3723431f;HASH9cba43bacdab5263c98545;\                  HASH12c88a01da6e8379df86a7;HASH9c86579a83e1a2e4cf9736;  \                  HASHdc2951a7ada1f36a6c3aca;HASHea4dda6bbc7cdeb4abfdee;  \                  HASHce55006513c47235ac38ba;HASH012a33acaa077c0e612b9351;\                  HASH010dd1e923a123826ae30e4b;HASHaf674616785679fed4b7ee;\                  HASH0147eef4b9d1cb135e096619;HASHe69b9dbaa83ffb045d963b;\                  HASH01abc61c646c8e7a8ce88b10;HASH5f9cd13678e21820e32f3a;\                  HASHe8cbba1594c72c98f9aa1b;HASH01292a2b7b6b60dec96298bc;\ ... </code> En la Figura <imgref figure_gdbm_database_for_the_gutenberg_collection> se muestra un extracto de la base de datos de informaciones de la colección elaborada durante la creación de la colección Gutenberg. El extracto se obtuvo utilizando la aplicación //db2txt// de Greenstone que convierte el formato binario de base de datos GDBM en forma textual. La Figura <imgref figure_gdbm_database_for_the_gutenberg_collection> contiene tres registros separados por líneas horizontales. El primero es una entrada de documento, los otros dos forman parte de la jerarquía creada por el clasificador //AZList// para los títulos de la colección. La primera línea de cada registro es su clave. El registro del documento guarda el título del libro, el autor y cualquier otro metadato proporcionado (o extraído) durante la creación de la colección. Contiene asimismo valores para uso interno: dónde se encuentran los archivos asociados con ese documento (< //archivedir// >) y el número de documento utilizado internamente por MG (< //docnum// >). En el campo < //contains// > se almacena una lista de elementos, separados por caracteres de punto y coma, que remiten a registros conexos en la base de datos. Para un registro de documento, el campo < //contains// > sirve para señalar las secciones subdivididas. Las subsiguientes claves de registro se forman concatenando la clave en curso con uno de los elementos secundarios (separado por un punto). El segundo registro de la Figura <imgref figure_gdbm_database_for_the_gutenberg_collection> es el nodo principal de la jerarquía de clasificación de t //ítulos a-z//. Sus elementos secundarios, a los que se accede a través del campo < //contains// >, incluyen //CL1.1//, //CL1.2//, //CL1.3// y así sucesivamente, y corresponden a las distintas páginas para las letras //A//, //B//, //C//, etc. Hay únicamente 24 elementos secundarios: el clasificador //AZList// fusionó las letras //Q-R// e //Y-Z// porque sólo incluían unos cuantos títulos. Los elementos secundarios en el campo < //contains// > del tercer registro, //CL1.1//, son los documentos propiamente dichos. Es posible establecer estructuras más complicadas: el campo < //contains// > puede incluir una combinación de documentos y otros nodos //CL//. Las claves relacionadas con la clave en curso se distinguen de las claves absolutas porque llevan comillas (“) antepuestas. === El uso de MG y GDBM para implementar un objeto Fuente (Source) === <imgcaption figure_api_for_mg_and_gdbm_based_version_of_sourceclass|%!-- id:896 --%API de la versión de //sourceclass// basada en MG y GDBM (abreviada) ></imgcaption> <code> class mggdbmsourceclass : public sourceclass { protected:   // Omitted, data fields that store:   //   collection specific file information   //   index substructure   //   information about parent   //   pointers to gdbm and mgsearch objects public:   mggdbmsourceclass ();   virtual ~mggdbmsourceclass ();   void set_gdbmptr (gdbmclass *thegdbmptr);   void set_mgsearchptr (searchclass *themgsearchptr);   void configure (const text_t &key, const text_tarray &cfgline);   bool init (ostream &logout);   bool translate_OID (const text_t &OIDin, text_t &OIDout,                       comerror_t &err, ostream &logout);   bool get_metadata (const text_t &requestParams,                        const text_t &refParams,                        bool getParents, const text_tset &fields,                        const text_t &OID, MetadataInfo_tmap &metadata,                        comerror_t &err, ostream &logout);   bool get_document (const text_t &OID, text_t &doc,                      comerror_t &err, ostream &logout); }; </code> El objeto que reúne MG y GDBM para efectuar una implementación de //sourceclass// se denomina //mggdbmsourceclass//. En la Figura <imgref figure_api_for_mg_and_gdbm_based_version_of_sourceclass> se presenta su API. Las dos nuevas funciones miembro //set_gdbmptr()// y //set_mgsearchptr()// almacenan punteros dirigidos hacia sus respectivos objetos, de modo que las implementaciones de //get_metadata()// y //get_document()// puedan acceder a las herramientas apropiadas para llevar a cabo su tarea. ==== El objeto Filtro (<i>Filter</i>) ==== <imgcaption figure_api_for_the_filter_base_class|%!-- id:899 --%API de la clase base de Filtro ></imgcaption> <code> class filterclass { protected:   text_t gsdlhome;   text_t collection;   text_t collectdir;   FilterOption_tmap filterOptions; public:   filterclass ();   virtual ~filterclass ();   virtual void configure (const text_t &key,                           const text_tarray &cfgline);   virtual bool init (ostream &logout);   // returns the name of this filter   virtual text_t get_filter_name ();   // returns the current filter options   virtual void get_filteroptions                                 (InfoFilterOptionsResponse_t &response,                                  comerror_t &err, ostream &logout);   virtual void filter (const FilterRequest_t &request,                        FilterResponse_t &response,                        comerror_t &err, ostream &logout); }; </code> La API de la clase de base para el objeto Filtro de la Figura <imgref figure_greenstone_runtime_system> se presenta en la Figura <imgref figure_api_for_the_filter_base_class>. Empieza con los campos de datos protegidos //gsdlhome//, //collection// y //collectdir//. Estos suelen emplearse en las clases que necesitan acceder a archivos de colecciones específicas: * //gsdlhome// es lo mismo que //GSDLHOME//, de modo que el objeto pueda localizar los archivos de Greenstone * //collection// es el nombre del directorio que corresponde a la colección * //collectdir// es el nombre de ruta completo del directorio de la colección (es necesario porque una colección no tiene por qué encontrarse necesariamente en la zona //GSDLHOME//). //mggdbsourceclass// es otra clase que incluye estos tres campos de datos. El proceso de inicialización utiliza las funciones miembro //configure()// e //init()// (que ya vimos en la función //sourceclass//). El propio objeto se alinea estrechamente con la porción de filtro que le corresponde en el protocolo; las funciones //get_filteroptions()// y //filter()//, en particular, coinciden exactamente. <imgcaption figure_how_a_filter_option_is_stored|%!-- id:906 --%Manera en que se almacena una opción de filtro ></imgcaption> <code> struct FilterOption_t {   void clear (); \ void check_defaultValue ();   FilterOption_t () {clear();}   text_t name;   enum type_t {booleant=0, integert=1, enumeratedt=2, stringt=3};   type_t type;   enum repeatable_t {onePerQuery=0, onePerTerm=1, nPerTerm=2};   repeatable_t repeatable;   text_t defaultValue;   text_tarray validValues; }; struct OptionValue_t {   void clear ();   text_t name;   text_t value; }; </code> Las dos clases que se muestran en la Figura <imgref figure_how_a_filter_option_is_stored> son fundamentales para las opciones de filtro. //FilterOption_t// almacena el //nombre (name)// de la opción, su //tipo (type)//, y si se puede repetir o no ( //repeatable)//. La interpretación de //validValues// depende del tipo de opción. Para un tipo booleano, el primer valor es //false// y el segundo //true//. Para un tipo entero, el primer valor es el número mínimo y el segundo el máximo. Para un tipo enumerado todos los valores están en la lista. Para un tipo de cadena se ignora el valor. Para las situaciones más simples, se emplea //OptionValue_t// que registra el //nombre// de la opción y su //valor// como //text_t//. Los objetos de búsqueda y de respuesta transmitidos como parámetros a //filterclass// se crean a partir de estas dos clases, utilizando matrices asociativas para almacenar un conjunto de opciones como las que se requieren para //InfoFilterOptionsResponse_t//. Para obtener más detalles véase //GSDLHOME/src/recpt/comtypes.h//. ==== Objetos Filtro heredados (<i>Inherited Filter</i>) ==== <imgcaption figure_inheritance_hierarchy_for_filter|%!-- id:910 --%Jerarquía de herencia para el objeto de filtro ></imgcaption> {{..:images:dev_fig_38.png?342x332&direct}} Tal como se muestra en la Figura <imgref figure_inheritance_hierarchy_for_filter>, los filtros utilizan dos niveles de herencia. En primer lugar, se establece una distinción entre filtros de búsqueda y consulta, y luego para los primeros se procede a una implementación específica basada en MG. Para que funcione correctamente, //mgqueryfilterclass// necesita acceder a MG a través de //mgsearchclass// y a GDBM a través de //gdbmclass//. //browsefilterclass// sólo necesita tener acceso a GDBM. Los punteros de estos objetos se guardan como campos de datos protegidos dentro de las respectivas clases. ==== El código del servidor de colección ==== A continuación presentamos los archivos de encabezado del directorio //GSDLHOME/src/colservr//. El nombre del archivo coincide por lo general con el nombre del objeto que define. <tblcaption table_table_1|##HIDDEN##></tblcaption> |< - 120 420 >| | **browsefilter.h** | Heredado de //filterclass//, este objeto proporciona acceso a GDBM (se explica más arriba). | | | | **collectserver.h** | Este objeto enlaza los objetos Filtro y de Fuente de una colección a fin de formar el objeto Colección representado en la Figura <imgref figure_greenstone_runtime_system>. | | | | **colservrconfig.h** | Función de apoyo para la lectura de los archivos específicos de una colección //etc/collect.cfg// e //index/build.cfg//. El primero es el archivo de configuración de la colección y el segundo es un archivo generado por el proceso de creación que registra la hora de la última creación llevada satisfactoriamente a cabo, una lista de las correspondencias de índice, el número de documentos indizados y su tamaño en bytes (sin comprimir). | | | | **filter.h** | La clase de base del objeto de filtro //filterclass// antes mencionado. | | | | **maptools.h** | Define una clase llamada //stringmap// que proporciona una correspondencia que recuerda el orden original de una correspondencia //text_t//, pero cuya consulta es rápida. Se utiliza en //mggdbmsourceclass// y //queryfilterclass//. | | | | **mggdbmsource.h** | Heredado de //sourceclass//, este objeto proporciona acceso a MG y GDBM (se explica más arriba). | | | | **mgppqueryfilter.h** | Heredado de //queryfilterclass//, este objeto proporciona una implementación de //QueryFilter// basada en MG++, una versión mejorada de MG escrita en C++. Obsérvese que Greenstone está configurado para utilizar MG por defecto, ya que MG++ se encuentra todavía en fase de desarollo. | | | | **mgppsearch.h** | Heredado de //searchclass//, este objeto proporciona une implementación del objeto Búsqueda utilizando MG++. Como //mgppqueryfilterclass//, tampoco se utiliza por defecto. | | | | **mgq.h** | Interfaz funcional para el paquete MG. Sus principales funciones son //mg_ask()// y //mg_results()//. | | | | **mgqueryfilter.h** | Heredado de //queryfilterclass//, este objeto proporciona una implementación de //QueryFilter// basada en MG. | | | | **mgsearch.h** | Heredado de //searchclass//, este objeto proporciona una implementación de Search <i/> (búsqueda) utilizando MG (se explica más arriba). | | | | **phrasequeryfilter.h** | Heredado de //mgqueryclass//, este objeto proporciona una clase de consulta basada en frases. No se utiliza en la instalación por defecto. En su lugar //mgqueryfilterclass// ofrece esta posibilidad a través del apoyo funcional de //phrasesearch.h//. | | | | **phrasesearch.h** | Función de apoyo para efectuar una búsqueda de grupos de palabras como operación posterior al tratamiento. | | | | **querycache.h** | Utilizado por //searchclass// y sus clases heredadas para almacenar en el campo caché los resultados de una consulta, a fin de que la generación de las páginas resultantes de búsquedas posteriores resulte más eficaz (se explica más arriba). | | | | **queryfilter.h** | Heredado de la clase base de filtro //filterclass//, este objeto establece una clase base para los objetos Filtro y Búsqueda (se explica más arriba). | | | | **queryinfo.h** | Ayuda para la búsqueda: estructuras de datos y objetos para guardar los parámetros de consulta, los resultados de documentos y las frecuencias de términos. | | | | **search.h** | La clase de base del objeto Búsqueda //searchclass// (se explica más arriba). | | | | **source.h** | La clase de base del objeto Fuente //sourceclass// (se explica más arriba). | ===== Protocolo ===== <tblcaption table_list_of_protocol_calls|Lista de llamadas del protocolo></tblcaption> |< - 132 397 >| | //get_protocol_name()// | Devuelve el nombre de este protocolo. Las opciones incluyen //nullproto//, //corbaproto// y //z3950proto//. Utilizado por las partes del sistema de ejecución que dependen del protocolo para decidir qué código ejecutar. | | //get_collection_list()// | Devuelve la lista de colecciones que este protocolo conoce. | | //has_collection()// | Devuelve el valor //true// ( //verdadero)// si el protocolo puede comunicar con la colección nombrada, es decir, si se encuentra en su lista de colecciones. | | //ping()// | Reenvía el valor //true (verdadero)// si se ha logrado establecer una conexión con la colección nombrada. En el caso del protocolo nulo, la implementación es idéntica a la de //has_collection()//. | | //get_collectinfo()// | Obtiene información general sobre la colección nombrada: fecha de la última creación, número de documentos que contiene, etc. Incluye también metadatos del archivo de configuración de la colección: texto de “Acerca de esta colección”, el icono de la colección que ha de utilizarse, etc. | | //get_filterinfo()// | Obtiene una lista de todos los objetos Filtro para la colección nombrada. | | //get_filteroptions()// | Obtiene todas las opciones para un objeto Filtro particular en la colección nombrada. | | //filter()// | Permite la búsqueda y la consulta. Para un tipo de filtro determinado y unas configuraciones de opciones, accede al contenido de las colecciones nombradas para producir un conjunto de resultados que es filtrado según las configuraciones de opciones. Los campos de datos devueltos dependen también de las configuraciones de opciones, por ejemplo: la frecuencia de los términos de consulta y los metadatos de documento. | | //get_document()// | Obtiene un documento o sección de un documento. | En el Cuadro <tblref table_list_of_protocol_calls> se presenta una lista de las llamadas de función al protocolo, y un resumen de cada una de ellas. Los ejemplos de la Sección [[#how_the_conceptual_framework_fits_together|how_the_conceptual_framework_fits_together]] ilustran la mayoría de esas llamadas. Las funciones no mencionadas anteriormente son //has_collection()//, //ping()//, //get_protocol_name()// y //get_filteroptions()//. Las dos primeras responden afirmativa o negativamente a las preguntas “¿existe la colección en este servidor?” y “¿se encuentra en funcionamiento?”, respectivamente. La finalidad de las otras dos es admitir protocolos múltiples en una arquitectura repartida en diferentes computadoras, y no sólo el ejecutable individual basado en el protocolo nulo que se menciona aquí. Una de ellas detecta qué protocolo se encuentra en uso. La otra permite que un recepcionista consulte al servidor de una colección para averiguar qué opciones admite, a fin de configurarse dinámicamente y sacar el máximo provecho de los servicios ofrecidos por un servidor particular. <imgcaption figure_null_protocol_api|%!-- id:971 --%API del protocolo nulo (abreviado) ></imgcaption> <code> class nullproto : public recptproto { public:   virtual text_t get_protocol_name ();   virtual void get_collection_list (text_tarray &collist,                         comerror_t &err, ostream &logout);   virtual void has_collection (const text_t &collection,                         bool &hascollection,                         comerror_t &err, ostream &logout);   virtual void ping (const text_t &collection,                         bool &wassuccess,                         comerror_t &err, ostream &logout);   virtual void get_collectinfo (const text_t &collection,                         ColInfoResponse_t &collectinfo,                         comerror_t &err, ostream &logout); virtual void get_filterinfo (const text_t &collection,                         InfoFiltersResponse_t &response,                         comerror_t &err, ostream &logout); virtual void get_filteroptions (const text_t &collection,                         const InfoFilterOptionsRequest_t &request,                         InfoFilterOptionsResponse_t &response,                         comerror_t &err, ostream &logout);   virtual void filter (const text_t &collection,                        FilterRequest_t &request,                        FilterResponse_t &response,                        comerror_t &err, ostream &logout);   virtual void get_document (const text_t &collection,                              const DocumentRequest_t &request,                              DocumentResponse_t &response,                              comerror_t &err, ostream &logout); }; </code> En la Figura <imgref figure_null_protocol_api> se muestra la API para el protocolo nulo. Se han omitido los comentarios y determinados detalles de nivel inferior (véase el archivo fuente //recpt/nullproto.h// para obtener más información al respecto). Este protocolo hereda de la clase de base //recptproto//. Se utiliza la herencia virtual a fin de poder admitir fácilmente más de un tipo de protocolo –incluso protocolos aún no concebidos– en el resto del código fuente. Ello es posible porque el objeto de clase de base //recptproto// se utiliza a lo largo de todo el código fuente, excepto en el punto de creación, donde especificamos la variedad concreta de protocolo que deseamos utilizar que, en este caso, es el protocolo nulo. Con la excepción de //get_protocol_name()//, que no toma parámetros y devuelve el nombre del protocolo como una cadena de texto codificada en Unicode, todas las funciones de protocolo incluyen un parámetro de error y un flujo de salida como argumentos finales. El parámetro de error registra cualquier error que se produce durante la ejecución de la llamada de protocolo y la secuencia de salida se utiliza a efectos de registro ( //logging//). Las funciones son del tipo //void//, esto es, no reenvían explícitamente la información en su instrucción final, sino que reenvían datos a través de parámetros designados como el parámetro de error antes mencionado. En algunos lenguajes de programación, estos subprogramas se definirían como “procedimientos” en vez de “funciones”, pero C++ no establece ninguna distinción sintáctica. La mayoría de las funciones toman el nombre de la colección como argumento. Tres de las funciones miembro, //get_filteroptions()//, //filter()// y //get_document()// siguen el esquema de proporcionar un parámetro de consulta y de recibir los resultados en un parámetro de respuesta Response. ===== El recepcionista ===== La capa final del modelo conceptual es el recepcionista. Una vez analizados los argumentos CGI, la actividad principal es la ejecución de una acción con la asistencia de los objetos Formato y Lenguaje de macros, que se explican más adelante. Aunque se representen como objetos en el marco conceptual, los objetos Formato y Lenguaje de macros no son estrictamente objetos en el sentido de C++. En realidad, el objeto de formato es una colección de estructuras de datos con un conjunto de funciones que operan en aquéllas, y el objeto de lenguaje de macros se crea en torno a //displayclass//, definido en //lib/display.h// con un apoyo de conversión de flujo procedente de //lib/gsdlunicode.h//. ==== Acciones ==== <tblcaption table_actions_in_greenstone|Las acciones en Greenstone></tblcaption> |< - 132 397 >| | //action// | Clase base para la herencia virtual. | | //authenaction// | Presta ayuda a la autenticación del usuario: pide al usuario una contraseña si no se ha introducido ninguna, comprueba su validez y obliga al usuario a introducir la contraseña de nuevo si transcurre demasiado tiempo entre dos accesos. | | //collectoraction// | Genera las páginas para el Colector. | | //documentaction// | Recupera documentos, secciones de documentos, porciones de la jerarquía de clasificación o información sobre formatos. | | //extlinkaction// | Lleva un usuario directamente a una dirección URL que es externa a la colección, generando eventualmente antes una página de alerta (según las //preferencias//). | | //pageaction// | Genera una página en conjunción con el lenguaje de macros. | | //pingaction// | Comprueba si una colección está en línea. | | //queryaction// | Efectúa una búsqueda. | | //statusaction// | Genera las páginas de administración. | | //tipaction// | Facilita de forma aleatoria una sugerencia al usuario. | | //usersaction// | Administra la adición, la supresión y la gestión de los accesos de usuario. | Greenstone admite las once acciones que se resumen en el Cuadro <tblref table_actions_in_greenstone>. <imgcaption figure_using_the_cgiargsinfoclass_from_pageactioncpp|%!-- id:1003 --%Utilización de //cgiargsinfoclass// desde //pageaction.cpp// %!-- withLineNumber --%></imgcaption> <code 1> cgiarginfo arg_ainfo; arg_ainfo.shortname = "a"; arg_ainfo.longname = "action"; arg_ainfo.multiplechar = true; arg_ainfo.argdefault = "p"; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); arg_ainfo.shortname = "p"; arg_ainfo.longname = "page"; arg_ainfo.multiplechar = true; arg_ainfo.argdefault = "home"; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); </code> Los argumentos CGI necesarios para una acción se declaran formalmente en la función constructora utilizando //cgiarginfo// (definida en //recpt/cgiargs.h//). En la Figura <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp> se muestra un extracto de la función constructora //pageaction//, que define el tamaño y las propiedades de los argumentos CGI //a// y //p//. Para cada argumento CGI, el constructor debe especificar su nombre corto (líneas 2 y 10), que es el nombre de la variable CGI propiamente dicha; un nombre largo (líneas 3 y 11) que se utiliza para proporcionar una descripción más elocuente de la acción; si representa un valor de carácter simple o múltiple (líneas 4 y 12); un posible valor por defecto (líneas 5 y 13); lo que ocurre cuando se proporcionan varios valores por defecto (líneas 6 y 14) (ya que pueden introducirse valores por defecto en los archivos de configuración); y si es preciso preservar o no el valor al final de esta acción (líneas 7 y 15). Puesto que está integrada en el código, se pueden generar automáticamente páginas Web que detallan esta información. La acción //statusaction// produce esta información, que puede visualizarse introduciendo la dirección URL de la página de administración de Greenstone. Las doce acciones heredadas se construyen en //main()//, la función de alto nivel del ejecutable //library//, cuya definición figura en el archivo //recpt/librarymain.cpp//. Es aquí también donde se forma el objeto Recepcionista (definido en //recpt/receptionist.cpp//). La responsabilidad de todas las acciones recae en el objeto de recepcionista, que las trata manteniendo, como un campo de datos, una matriz asociativa de la clase base Acción, indizada por nombres de acción. <imgcaption figure_action_base_class_api|%!-- id:1008 --%API de la clase base Acción ></imgcaption> <code> class action { protected:   cgiargsinfoclass argsinfo;   text_t gsdlhome; public:   action ();   virtual ~action ();   virtual void configure (const text_t &key,                           const text_tarray &cfgline);   virtual bool init (ostream &logout);   virtual text_t get_action_name ();   cgiargsinfoclass getargsinfo ();   virtual bool check_cgiargs (cgiargsinfoclass &argsinfo,                               cgiargsclass &args,                               ostream &logout); virtual bool check_external_cgiargs (cgiargsinfoclass &argsinfo,                               cgiargsclass &args,                               outconvertclass &outconvert,                               const text_t &saveconf,                               ostream &logout); virtual void get_cgihead_info (cgiargsclass &args,                               recptprotolistclass *protos,                               response_t &response,                               text_t &response_data,                               ostream &logout); virtual bool uses_display (cgiargsclass &args); virtual void define_internal_macros (displayclass &disp,                               cgiargsclass &args,                               recptprotolistclass *protos,                               ostream &logout); virtual void define_external_macros (displayclass &disp,                               cgiargsclass &args,                               recptprotolistclass *protos,                               ostream &logout); virtual bool do_action (cgiargsclass &args,                         recptprotolistclass *protos,                         browsermapclass *browsers,                         displayclass &disp,                         outconvertclass &outconvert,                         ostream &textout,                         ostream &logout); }; </code> En la Figura <imgref figure_action_base_class_api> se muestra la API de la clase base Acción. Cuando se ejecuta una acción, el objeto Recepcionista activa diversas funciones, empezando por //check_cgiarsgs()//. La mayoría de estas funciones contribuyen a verificar, configurar y definir valores y macros, mientras que //do_action()// genera efectivamente la página resultante. Si un objeto heredado particular no dispone de una definición para una función miembro determinada, pasa a la definición de la clase base que implementa un comportamiento por defecto apropiado. La explicación de las funciones miembro es la siguiente: * //get_action_name()// devuelve el valor del argumento CGI //a// que especifica esta acción. El nombre debería ser corto pero puede incluir más de un carácter. * //check_cgiargs()// es llamada antes que //get_cgihead_info()//, //define_external_macros()// y //do_action()//. En caso de error aparece un mensaje escrito en //logout// ; si el error es importante, la función devuelve el valor //false// (falso) y no genera ningún contenido de página. * //check_external_cgiargs()// se activa después de //check_cgiargs()// para todas las acciones. Su uso se limita a invalidar otros comportamientos por defecto, como por ejemplo, mostrar una página de conexión cuando la página solicitada requiere una autenticación. * //get_cgihead_info()// establece la información del encabezado CGI. Si //response// tiene el valor de //location//, entonces //response_data// contiene la dirección de redirección. Si //response// equivale a //content//, entonces //response_data// contiene el tipo de contenido. * //uses_display()// devuelve //true// ( //verdadero)// si se necesita //displayclass// para mostrar el contenido de la página (comportamiento por defecto). * //define_internal_macros()// define todas las macros relacionadas con las páginas generadas por esta acción. * //define_external_macros()// define todas las macros que podrían ser utilizadas por otras acciones para producir páginas. * //do_action()// genera la página resultante, normalmente en un flujo a través del objeto Lenguaje de macros //display// y el objeto de conversión de salida //textout//. Reenvía el valor //false// si un error ha impedido que la acción produzca un resultado. Situado en el principio de la definición de clase, //argsinfo// es el campo de datos protegidos (utilizado en el extracto de código que se muestra en la Figura <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp>) que almacena la información de argumento CGI especificada en una función constructora de acción heredada. El otro campo de datos, //gsdlhome//, registra //GSDLHOME// para facilitar el acceso((El valor de //gsdlhome// procede del archivo //gsdlsite.cfg// ubicado en el mismo directorio que el ejecutable CGI //library//, mientras que //GSDLHOME// se activa ejecutando el guión //setup// que da acceso a un archivo diferente; así pues, es posible técnicamente que los dos valores sean diferentes, pero no es deseable. El texto que figura más arriba está escrito partiendo de la hipótesis de que ambos valores son iguales.)). El objeto incluye asimismo las funciones //configure()// e //init()// a efectos de inicialización. ==== Formateo ==== <imgcaption figure_core_data_structures_in_format|%!-- id:1021 --%Estructuras de datos centrales de la clase Formato ></imgcaption> <code> enum command_t {comIf, comOr, comMeta, comText, comLink, comEndLink,                 comNum, comIcon, comDoc,                 comHighlight, comEndHighlight}; enum pcommand_t {pNone, pImmediate, pTop, pAll}; enum dcommand_t {dMeta, dText}; enum mcommand_t {mNone, mCgiSafe}; struct metadata_t {   void clear();   metadata_t () {clear();}   text_t metaname;   mcommand_t metacommand;   pcommand_t parentcommand;   text_t parentoptions; }; // The decision component of an {If}{decision,true-text,false-text} // formatstring. The decision can be based on metadata or on text; // normally that text would be a macro like // _cgiargmode_. struct decision_t {   void clear();   decision_t () {clear();}   dcommand_t command;   metadata_t meta;   text_t text; }; struct format_t {   void clear();   format_t () {clear();}   command_t command;   decision_t decision;   text_t text;   metadata_t meta;   format_t *nextptr;   format_t *ifptr;   format_t *elseptr;   format_t *orptr; }; </code> Aunque en la Figura <imgref figure_greenstone_runtime_system> el formateo se representa como una entidad única, en realidad está constituida por un conjunto de estructuras de datos y funciones, que se encuentran en el archivo de encabezado //recpt/formattools.h//. Las estructuras de datos centrales aparecen en la Figura <imgref figure_core_data_structures_in_format>. <imgcaption figure_data_structures_built_for_sample_format_statement|%!-- id:1023 --%Estructuras de datos creadas para el ejemplo de instrucción de formato ></imgcaption> {{..:images:dev_fig_43.png?605x187&direct}} Es más fácil explicar la implementación con un ejemplo. Cuando la instrucción de formato: <code>        Format CL1Vlist “[link][Title]{If}{[Creator], por [Creator]}[/link]}” </code> es leída desde un archivo de configuración de colección, las funciones de //formattools.cpp// la analizan y se forma la estructura de datos interconectados que se muestra en la Figura <imgref figure_data_structures_built_for_sample_format_statement>. Cuando es necesario que una acción evalúe la instrucción de formato, se recorre la estructura de datos. La ruta que se toma a nivel de los nodos //comIf// y //comOr// depende de los metadatos devueltos por una llamada al protocolo. Al recuperar los metadatos, puede ocurrir que el resultado contenga otras macros y sintaxis de formato. Esto se resuelve avanzando y retrocediendo entre el análisis y la evaluación, según convenga. ==== El lenguaje de macros ==== La entidad de Lenguaje de macros de la Figura <imgref figure_greenstone_runtime_system>, al igual que la de Formato, no corresponde a una única clase C++. En este caso existe una clase principal, pero la implementación del lenguaje de macros depende también de funciones y clases de apoyo. La mejor manera de explicarlo es, una vez más, mediante un ejemplo. En primer lugar presentaremos unos ejemplos de definiciones de macros que ilustran las prioridades de las macros, luego, mediante un diagrama, describiremos las estructuras de datos principales creadas para apoyar esta actividad. Por último, presentaremos y explicaremos las funciones miembro públicas de //displayclass//, el objeto de macro de primer nivel. <imgcaption figure_illustration_of_macro_precedence|%!-- id:1031 --%Ilustración de la prioridad entre las macros ></imgcaption> <code> package query _header_ []                           {_querytitle_} _header_ [l=en]                       {Search page} _header_ [c=demo]           {<table bgcolor=green><tr><td>_querytitle_</td></tr></table>} _header_ [v=1]                        {_textquery_} _header_ [l=fr,v=1,c=hdl]   {HDL Page de recherche} </code> En una colección Greenstone clásica, la prioridad entre las macros es normalmente: //c// (para la colección) tiene prioridad sobre //v// (para la interfaz gráfica o textual), que tiene prioridad sobre //l// (para el idioma). Esto se establece con la línea: <code> macroprecedence c, v, l </code> en el archivo de configuración principal //main.cfg//. Las instrucciones de macros de la Figura <imgref figure_illustration_of_macro_precedence> definen ejemplos de macros para _ //header// _ en el paquete //query// para distintos valores de //c//, //v// y //l//. Si los argumentos CGI proporcionados cuando se solicita una acción incluyen //c=hdl//, //v=1// y //l=en//, se seleccionaría entonces la presentación de la macro _ //header// _ //[v=1]//. Se seleccionaría antes que _ //content// _ //[l=en]// porque //v// tiene prioridad sobre //l//. La macro _ //content// _ //[l=fr, v=1, c=hdl]// no sería seleccionada porque el parámetro de página para //l// es diferente. <imgcaption figure_data_structures_representing_the_default_macros|%!-- id:1034 --%Estructuras de datos que representan las macros por defecto ></imgcaption> {{..:images:dev_fig_45.png?605x224&direct}} En la Figura <imgref figure_data_structures_representing_the_default_macros> se muestra la estructura de datos principales creada por la lectura de los archivos de macros especificados en //etc/main.cfg//. Se trata esencialmente de una matriz asociativa de matrices asociativas de matrices asociativas. El nivel superior (que aparece a la izquierda) indiza el paquete del que procede la macro, y el segundo nivel indiza el nombre de la macro. El último nivel indiza todos los parámetros especificados, guardando cada uno de ellos como tipo //mvalue// que registra el valor de la macro y el archivo del que procede. Por ejemplo, podemos ver el texto definido para _ //header// _ //[l=en]// de la Figura <imgref figure_illustration_of_macro_precedence> almacenado en el más bajo de los dos registros //mvalue// de la Figura <imgref figure_data_structures_representing_the_default_macros>. <imgcaption figure_displayclass_api|%!-- id:1036 --%API de la clase //displayclass// (abreviada) ></imgcaption> <code> class displayclass { public:   displayclass ();   ~displayclass ();   int isdefaultmacro (text_t package, const text_t ¯oname);   int setdefaultmacro (text_t package, const text_t ¯oname,                        text_t params, const text_t ¯ovalue);   int loaddefaultmacros (text_t thisfilename);   void openpage (const text_t &thispageparams,   const text_t &thisprecedence);   void setpageparams (text_t thispageparams,                       text_t thisprecedence);   int setmacro (const text_t ¯oname,                 text_t package,                 const text_t ¯ovalue);   void expandstring (const text_t &inputtext, text_t &outputtext);   void expandstring (text_t package, const text_t &inputtext,                      text_t &outputtext, int recursiondepth = 0);   void setconvertclass (outconvertclass *theoutc) {outc = theoutc;}                         outconvertclass *getconvertclass () {return outc;}                         ostream *setlogout (ostream *thelogout); }; </code> El objeto principal de apoyo al lenguaje de macros es //displayclass//, definido en //lib/display.h//. Sus funciones miembro públicas aparecen en la Figura <imgref figure_displayclass_api>. La clase lee los archivos de macros especificados utilizando //loaddefaultmacros()// y guarda en una sección protegida de la clase (que no se muestra) el tipo de la estructura de datos que se muestra en la Figura <imgref figure_data_structures_representing_the_default_macros>. Es posible asimismo que el sistema de ejecución cree macros utilizando //setmacro()// (en el último ejemplo de la Sección [[#how_the_conceptual_framework_fits_together|how_the_conceptual_framework_fits_together]], //pageaction// establece que _ //homeextra// _ sea el cuadro generado dinámicamente para las colecciones disponibles que utilizan esta función). Todo ello con la ayuda de un conjunto de matrices asociativas semejantes a las que se utilizan para representar los archivos de macros (no es idéntico porque el primero no requiere la capa “parámetro”). En //displayclass//, las macros que se leen desde el archivo se denominan //macros por defecto//. Las macros locales que se especifican a través de //setmacro()// se denominan //macros en curso//, y son borradas de la memoria tras generarse la página. Cuando se va a generar una página, se llama primero //openpage()// para comunicar las configuraciones en curso de los parámetros de la página en cuestión ( //l=en//, etc.). A continuación, el texto y las macros fluyen a través de la clase – normalmente desde una //actionclass// – utilizando el código: <code> cout << text_t2ascii << display << “_unamacro_”                                 << “_otramacro_”; </code> El resultado es que las macros se amplían siguiendo las configuraciones de los parámetros de la página. En caso necesario, estas configuraciones pueden modificarse durante el proceso mediante una acción que utiliza //setpageparams()//. Las demás funciones miembro públicas proporcionan el apoyo de nivel inferior. ==== El código del recepcionista ==== Hemos explicado ya los objetos principales del recepcionista. A continuación, detallaremos las clases de apoyo que se encuentran en el directorio //GSDLHOME/src/recpt//. Excepto cuando la eficiencia es prioritaria, en cuyo caso las definiciones están //inline// (en línea), los detalles de la implementación se encuentran en el archivo //.cpp// correspondiente al archivo de encabezados. Los archivos de apoyo suelen incluir la palabra //tool// como parte del nombre del archivo, como en //OIDtools.h// y //formattools.h//. Un segundo conjunto de archivos de alcance léxico comprenden el prefijo //z3950//. Los archivos proporcionan acceso remoto a las bases de datos y los catálogos en línea públicamente accesibles mediante el protocolo Z39.50. Otro amplio grupo de archivos de apoyo comprende el término //browserclass//. Estos archivos están relacionados mediante una jerarquía de herencia virtual. Como grupo apoyan la noción abstracta de consulta: generación de páginas en serie que comportan contenidos de documentos compartimentados o metadatos. Las posibilidades de consulta incluyen el examen de documentos ordenados alfabéticamente por título o cronológicamente por fecha, la consulta de los resultados de una búsqueda por grupos de diez, y el acceso a las páginas individuales de un libro utilizando el mecanismo “ir a la página”. Todas las actividades de consulta heredan la clase de base //browserclass// : * //datelistbrowserclass// para apoyo para las listas cronológicas; * //hlistbrowserclass// para apoyo para las listas horizontales; * //htmlbrowserclass// para apoyo para las páginas HTML; * //invbrowserclass// para apoyo para las listas invisibles; * //pagedbrowserclass// para apoyo para ir a una página particular; * //vlistbrowserclass// para apoyo para las listas verticales. Las acciones acceden a los objetos //browserclass// a través del archivo //browsetools.h//. <tblcaption table_table_2|##HIDDEN##></tblcaption> |< - 140 390 >| | **OIDtools.h** | Función de apoyo para la evaluación de los identificadores de documento en el protocolo. | | **action.h** | Clase de base para la entidad Acción expuesta en la Figura <imgref figure_greenstone_runtime_system>. | | **authenaction.h** | Acción heredada para la gestión de la autenticación de un usuario. | | **browserclass.h** | Clase de base para las actividades de consulta abstractas. | | **browsetools.h** | Función de apoyo para el acceso a la jerarquía //browserclass//. Las funciones incluyen la ampliación y contracción de contenidos, la creación de un índice y la generación de controles como el mecanismo “ir a la página”. | | **cgiargs.h** | Define //cgiarginfo//, utilizado en la Figura <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp>, así como el apoyo de otras estructuras de datos para los argumentos CGI. | | **cgiutils.h** | Función de apoyo para los argumentos CGI que utilizan las estructuras de datos definidas en //cgiargs.h//. | | **cgiwrapper.h** | Función de apoyo que hace todo lo necesario para generar una página utilizando el protocolo CGI. El acceso se hace mediante la función: \\ ''void cgiwrapper (receptionist &recpt,'' \\ ''text_t collection);'' \\ que es la única función declarada en el archivo de encabezados. Todo el resto del archivo //.cpp// tiene un alcance léxico de carácter local en el archivo (utilizando la palabra clave C++ //static//). Si se utiliza la función para una colección particular, entonces se debe introducir //collection//, o si no la cadena vacía “”. El código incluye apoyo para Fast-CGI. | | **collectoraction.h** | Acción heredada que facilita la creación de colecciones por parte del usuario final mediante el Colector. La página generada procede de //collect.dm// y es controlada por el argumento CGI //p=page//. | | **comtypes.h** | Tipos centrales del //protocolo//. | | **converter.h** | Objeto de apoyo para los convertidores de flujo. | | **datelistbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para la consulta de las listas cronológicas, como las que se ven en la colección //Archivos Greenstone// en “ //dates// ” (fechas) de la barra de desplazamiento. | | **documentaction.h** | Acción heredada que se utiliza para recuperar un documento o porción de una jerarquía de clasificación. | | **extlinkaction.h** | Acción heredada que controla si un usuario va directamente a un enlace externo o si debe pasar por una página de aviso que le informa de que se dispone a salir del sistema de biblioteca digital. | | **formattools.h** | Función de apoyo para analizar y evaluar las instrucciones //format// de configuración de colección. Se explica con más detalles en la Sección [[##formatting|formatting]] //supra//. | | **historydb.h** | Estructuras de datos y función de apoyo para la gestión de una base de datos de búsquedas anteriores, de modo que un usuario pueda iniciar una nueva búsqueda que incluya términos de búsquedas anteriores. | | **hlistbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para la consulta de las listas horizontales. | | **htmlbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para la consulta de las páginas HTML. | | **htmlgen.h** | Función de apoyo para resaltar los términos de búsqueda en una cadena //text_t//. | | **htmlutils.h** | Función de apoyo que convierte una cadena //text_t// en el HTML equivalente. Los símbolos “, &, < y > se convierten respectivamente en //"//, //&//, //<// y //>//. | | **infodbclass.h** | Define dos clases: //gdbmclass// e //infodbclass//. La primera proporciona la API de Greenstone para GDBM; la segunda es la clase de objeto utilizada para guardar una entrada de registro leída en una base de datos GDBM, y es esencialmente una matriz asociativa formada por matrices de cadenas //text_t// indizadas por números enteros. | | **invbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para la consulta de listas que no se presentarán en pantalla (invisibles). | | **nullproto.h** | Heredada de //recptproto//, este clase realiza el protocolo nulo a través de las llamadas de funciones del recepcionista al servidor de colección. | | **pageaction.h** | Acción heredada que, en combinación con el archivo de macros indicado en //p=page//, genera una página Web. | | **pagedbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para el mecanismo “ir a la página”, como vimos por ejemplo en la colección Gutenberg. | | **pingaction.h** | Acción heredada que comprueba si una colección particular responde. | | **queryaction.h** | Acción heredada que toma la búsqueda formulada, las configuraciones y las preferencias, efectúa la búsqueda y genera como resultado el subconjunto de documentos correspondientes //o=num// empezando en la posición //r=num//. | | **querytools.h** | Función de apoyo para las búsquedas. | | **receptionist.h** | Objeto de alto nivel del recepcionista. Mantiene un registro de las informaciones de argumentos CGI, las instancias de cada acción heredada, las instancias de cada objeto de consulta heredado, el objeto central de lenguaje de macros //displayclass//, así como todos los convertidores posibles. | | **recptconfig.h** | Función de apoyo para la lectura de los principales archivos de configuración y de sitio. | | **recptproto.h** | Clase de base para el protocolo. | | **statusaction.h** | Acción heredada que genera, conjuntamente con //status.dm//, las diversas páginas de administración. | | **tipaction.h** | Acción heredada que genera, conjuntamente con //tip.dm//, una página Web que contiene una sugerencia elegida de forma aleatoria a partir de una lista de sugerencias que se encuentra en //tip.dm//. | | **userdb.h** | Estructura de datos y función de apoyo para mantener una base de datos GDBM de usuarios: su contraseña, sus grupos, etc. | | **usersaction.h** | Una acción de administración heredada de la clase de base que permite añadir y suprimir usuarios, así como modificar los grupos a los que pertenecen. | | **vlistbrowserclass.h** | Heredado de //browserclass//, este objeto presta apoyo para la consulta de las listas verticales, que constituyen la base de los clasificadores. Por ejemplo, los elementos secundarios del nodo de títulos que empiezan con la letra //N// forman una //Vlist//. | | **z3950cfg.h** | Apoyo de estructura de datos para el protocolo Z39.50. Utilizado por //z3950proto.cpp//, que define la clase de protocolo principal (heredada de la clase de base //recptproto//) y el analizador del archivo de configuración //zparse.y// (escrito en YACC). | | **z3950proto.h** | Heredado de //recptproto//, esta clase implementa el protocolo Z39.50 de tal modo que el recepcionista de Greenstone pueda acceder a los sitios de bibliotecas remotas que funcionan con servidores Z39.50. | | **z3950server.h** | Apoyo complementario para el protocolo Z39.50. | ===== Inicialización ===== En Greenstone, la inicialización es una operación compleja que trata archivos de configuración y asigna valores por defecto a los campos de datos. Además de las funciones de herencia y de creación, los objetos centrales definen las funciones //init()// y //configure()// para contribuir a normalizar la tarea. Aun así, el orden de ejecución puede ser difícil de seguir. En esta sección se explica lo que sucede. Greenstone utiliza varios archivos de configuración con diferentes fines, pero todos respetan la misma sintaxis. A menos que una línea empiece por el símbolo “#” o sólo contenga espacios en blanco, la primera palabra define un término clave y las demás representan una configuración particular de dicho término clave. Las líneas de los archivos de configuración se transmiten de una en una a la función //configure()// y contienen dos argumentos: el término clave y una matriz de las palabras restantes. Basándose en el término clave, una versión particular de //configure()// determina si la información es de interés, y si es así la guarda. Por ejemplo, //collectserver// (que corresponde al objeto Colección de la Figura <imgref figure_greenstone_runtime_system>) trata las instrucciones de formato del archivo de configuración de una colección. Cuando la función //configure()// recibe el término clave //format//, se activa una instrucción //if// que guarda en el objeto una copia del segundo argumento de la función. Tras tratar el término clave y antes de que concluya la función, algunas versiones de //configure()// transmiten los datos a las funciones //configure()// de otros objetos. El objeto Recepcionista activa la función //configure()// de los objetos Acciones, Protocolos y Consulta. El objeto Protocolo nulo activa la función //configure()// de cada objeto Colección; el objeto Colección activa los objetos Filtro y Fuente. En C++, los campos de datos se inicializan normalmente mediante la función de creación del objeto. Sin embargo, en Greenstone algunas inicializaciones dependen de los valores leídos en los archivos de configuración, por ello es preciso proceder a una segunda tanda de inicializaciones. Esta es la finalidad de las funciones miembro //init()//, y en algunos casos requiere posteriores llamadas de la función //configure()//. <imgcaption figure_initialising_greenstone_using_the_null_protocol|%!-- id:1136 --%Inicialización de Greenstone utilizando el protocolo nulo ></imgcaption> <code> ============ Main program ============ Statically construct Receptionist Statically construct NullProtocol Establish the value for ’gsdlhome’ by reading gsdlsite.cfg Foreach directory in GSDLHOME/collect that isn’t "modelcol":   Add directory name (now treated as collection name) to NullProtocol:     Dynamically construct Collection     Dynamically construct Gdbm class     Dynamically construct the Null Filter     Dynamically construct the Browse Filter     Dynamically construct MgSearch     Dynamically construct the QueryFilter     Dynamically construct the MgGdbmSource     Configure Collection with ’collection’       Passing ’collection’ value on to Filters and Sources: Configure Receptionist with ’collectinfo’:       Passing ’collectinfo’ value on to Actions, Protocols, and Browsers: Add NullProtocol to Receptionist Add in UTF-8 converter Add in GB converter Add in Arabic converter Foreach Action:   Statically construct Action   Add Action to Receptionist Foreach Browsers:   Statically construct Browser   Add Browser to Receptionist Call function cgiwrapper:   =================   Configure objects   =================   Configure Receptionist with ’collection’     Passing ’collection’ value on to Actions, Protocols, and Browsers:     NullProtocol not interested in ’collection’   Configure Receptionist with ’httpimg’     Passing ’httpimg’ value on to Actions, Protocols, and Browsers:     NullProtocol passing ’httpimg’ on to Collection     Passing ’httpimg’ value on to Filters and Sources:   Configure Receptionist with ’gwcgi’     Passing ’gwcgi’ value on to Actions, Protocols, and Browsers:     NullProtocol passing ’gwcgi’ on to Collection     Passing ’gwcgi’ value on to Filters and Sources:   Reading in site configuration file gsdlsite.cfg     Configure Recptionist with ’gsdlhome’       Passing ’gsdlhome’ value on to Actions, Protocols, and Browsers:       NullProtocol passing ’gsdlhome’ on to Collection         Passing ’gsdlhome’ value on to Filters and Sources:     Configure Recptionist with ...     ... and so on for all entries in gsdlsite.cfg   Reading in main configuration file main.cfg     Configure Recptionist with ...     ... and so on for all entries in main.cfg   ====================   Initialising objects   ====================   Initialise the Receptionist     Configure Receptionist with ’collectdir’       Passing ’collectdir’ value on to Actions, Protocols, and Browsers:       NullProtocol not interested in ’collectdir’     Read in Macro files     Foreach Actions       Initialise Action     Foreach Protocol       Initialise Protocol       When Protocol==NullProtocol:         Foreach Collection           Reading Collection’s build.cfg           Reading Collection’s collect.cfg             Configure Collection with ’creator’               Passing ’creator’ value on to Filters and Sources:             Configure Collection with ’maintainer’               Passing ’maintainer’ value on to Filters and Sources:             ... and so on for all entries in collect.cfg     Foreach Browsers       Initialise Browser   =============   Generate page   =============   Parse CGI arguments   Execute designated Action to produce page End. </code> En la Figura <imgref figure_initialising_greenstone_using_the_null_protocol> se presentan los mensajes de diagnóstico generados por una versión de Greenstone configurada para resaltar el proceso de inicialización. El programa arranca con la función //main()// del archivo //recpt/librarymain.cpp//. Crea un objeto Recepcionista y un objeto Protocolo nulo, luego recorre el archivo //gsdlsite.cfg// (ubicado en el mismo directorio que el ejecutable //library//) en  busca de //gsdlhome// y guarda su valor en una variable. Para cada colección en línea –lista establecida leyendo los directorios de //GSDLHOME/collect// – el programa crea un objeto de colección mediante el objeto Protocolo nulo, que contiene objetos Filtro, Búsqueda y Fuente, así como algunas llamadas cableadas a //configure()//. A continuación, la función //main()// agrega el objeto Protocolo nulo al recepcionista, que mantiene una matriz de clase de base de protocolos en un campo de datos protegidos, y luego activa varios convertidores. La función //main()// crea todos los objetos Acción y Consulta que se utilizan en el ejecutable y los incorpora al recepcionista. La función concluye al activar la función //cgiwrapper()// ubicada en //cgiwrapper.cpp//, que efectúa a su vez un número importante de inicializaciones de objetos. La función //cgiwrapper()// comprende tres partes: configuración, inicialización y generación de página. En primer lugar, se efectúan algunas llamadas cableadas a la función //configure()//, luego se lee el archivo //gsdlsite.cfg// y se aplica //configure// a cada línea. Lo mismo ocurre con el archivo //etc/main.cfg//. La segunda fase de //cgiwrapper()// activa //init()//. El objeto Recepcionista sólo hace una llamada a su función //init()//, pero esta acción invoca las funciones //init()// de los diferentes objetos que contiene. Primero una llamada cableada a //configure()// para instalar //collectdir//, y luego se leen los archivos de macros. Se activa la función //init()// de cada acción. Lo mismo ocurre con cada protocolo almacenado en el recepcionista, pero en el sistema que se describe aquí sólo se almacena el protocolo nulo. La activación de la función //init()// de este objeto suscita otras configuraciones: en cada colección del protocolo nulo se leen y se tratan los archivos específicos de la colección //build.cfg// y //collect.cfg//, y cada línea activa la función //configure()//. La fase final de //cgiwrapper()// consiste en analizar los argumentos CGI y luego activar la acción adecuada. Ambas acciones se efectúan con la asistencia del objeto Recepcionista. Los códigos de configuración, inicialización y generación de páginas están separados porque Greenstone está concebido para funcionar como servidor (con Fast-CGI, el protocolo CORBA, o la versión Biblioteca Local de Windows). En ese modo de funcionamiento, el código de configuración y de inicialización sólo se ejecuta una vez, y luego el programa permanece en memoria y genera numerosas páginas Web en respuesta a las consultas de los usuarios sin necesidad de volverse a inicializar.