Revision: 881 http://geany-plugins.svn.sourceforge.net/geany-plugins/?rev=881&view=rev Author: ctabin Date: 2009-08-09 20:56:04 +0000 (Sun, 09 Aug 2009)
Log Message: ----------- Plugin pretty-printer for XML added
Added Paths: ----------- trunk/pretty-printer/ trunk/pretty-printer/src/ trunk/pretty-printer/src/Plugin.c trunk/pretty-printer/src/PrettyPrinter.c trunk/pretty-printer/src/PrettyPrinter.h
Added: trunk/pretty-printer/src/Plugin.c =================================================================== --- trunk/pretty-printer/src/Plugin.c (rev 0) +++ trunk/pretty-printer/src/Plugin.c 2009-08-09 20:56:04 UTC (rev 881) @@ -0,0 +1,147 @@ +/** + * Written by Cédric Tabin + * http://www.astorm.ch/ + * Version 1.0 - 08.08.2009 + * + * Code under licence GPLv2 + * Geany - http://www.geany.org/ + */ + +/* + * Basic plugin structure, based of Geany Plugin howto : + * http://www.geany.org/manual/reference/howto.html + */ + +#include <stdlib.h> +#include <stdio.h> +#include <geany.h> +#include <ui_utils.h> +#include <plugindata.h> +#include <editor.h> +#include <document.h> +#include <filetypes.h> +#include <geanyfunctions.h> +#include <Scintilla.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include "PrettyPrinter.c" + +GeanyPlugin* geany_plugin; +GeanyData* geany_data; +GeanyFunctions* geany_functions; +PrettyPrintingOptions* prettyPrintingOptions; + +//plugin information +PLUGIN_VERSION_CHECK(130) +PLUGIN_SET_INFO("XML Formatter", "Format an XML file", + "1.0", "Cédric Tabin - http://www.astorm.ch"); + +static GtkWidget *main_menu_item = NULL; + +static void item_activate_cb(GtkMenuItem *menuitem, gpointer gdata) +{ + //default printing options + if (prettyPrintingOptions == NULL) { prettyPrintingOptions = createDefaultPrettyPrintingOptions(); } + + //retrieves the current document + GeanyDocument* doc = document_get_current(); + GeanyEditor* editor = doc->editor; + ScintillaObject* sco = editor->sci; + + //allocate a new pointer + int length = sci_get_length(sco)+1; + char* buffer = (char*)malloc(length*sizeof(char)); + if (buffer == NULL) { exit(-1); } //malloc error + + //retrieves the text + sci_get_text(sco, length, buffer); + + //checks if the data is an XML format + xmlDoc* xmlDoc = xmlParseDoc((unsigned char*)buffer); + + if(xmlDoc == NULL) //this is not a valid xml + { + dialogs_show_msgbox(GTK_MESSAGE_ERROR, "Unable to parse the content as XML."); + return; + } + + //process pretty-printing + int result = processXMLPrettyPrinting(&buffer, &length, prettyPrintingOptions); + if (result != 0) + { + dialogs_show_msgbox(GTK_MESSAGE_ERROR, "Unable to process PrettyPrinting on the specified XML because some features are not supported."); + return; + } + + //updates the document + sci_set_text(sco, buffer); + + //sets the type + GeanyFiletype* fileType = filetypes_index(GEANY_FILETYPES_XML); + document_set_filetype(doc, fileType); + + //free all + xmlFreeDoc(xmlDoc); +} + +void plugin_init(GeanyData *data) +{ + //Initializes the libxml2 + LIBXML_TEST_VERSION + + //put the menu into the Tools + main_menu_item = gtk_menu_item_new_with_mnemonic("XML Formatter"); + gtk_widget_show(main_menu_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), main_menu_item); + + //add activation callback + g_signal_connect(main_menu_item, "activate", G_CALLBACK(item_activate_cb), NULL); +} + +void plugin_cleanup(void) +{ + //destroys the plugin + gtk_widget_destroy(main_menu_item); +} + +GtkWidget* plugin_configure(GtkDialog * dialog) +{ + //default printing options + if (prettyPrintingOptions == NULL) { prettyPrintingOptions = createDefaultPrettyPrintingOptions(); } + + GtkWidget* globalBox = gtk_hbox_new(TRUE, 4); + GtkWidget* rightBox = gtk_vbox_new(FALSE, 6); + GtkWidget* centerBox = gtk_vbox_new(FALSE, 6); + GtkWidget* leftBox = gtk_vbox_new(FALSE, 6); + + GtkWidget* textLabel = gtk_label_new("Text nodes"); + GtkWidget* textOneLine = gtk_check_button_new_with_label("One line"); + GtkWidget* textInline = gtk_check_button_new_with_label("Inline"); + + GtkWidget* cdataLabel = gtk_label_new("CDATA nodes"); + GtkWidget* cdataOneLine = gtk_check_button_new_with_label("One line"); + GtkWidget* cdataInline = gtk_check_button_new_with_label("Inline"); + + GtkWidget* commentLabel = gtk_label_new("Comments"); + GtkWidget* commentOneLine = gtk_check_button_new_with_label("One line"); + GtkWidget* commentInline = gtk_check_button_new_with_label("Inline"); + + gtk_box_pack_start(GTK_BOX(globalBox), leftBox, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(globalBox), centerBox, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(globalBox), rightBox, FALSE, FALSE, 3); + + gtk_box_pack_start(GTK_BOX(leftBox), textLabel, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(centerBox), textOneLine, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(rightBox), textInline, FALSE, FALSE, 3); + + gtk_box_pack_start(GTK_BOX(leftBox), cdataLabel, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(centerBox), cdataOneLine, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(rightBox), cdataInline, FALSE, FALSE, 3); + + gtk_box_pack_start(GTK_BOX(leftBox), commentLabel, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(centerBox), commentOneLine, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(rightBox), commentInline, FALSE, FALSE, 3); + + gtk_widget_show_all(globalBox); + return globalBox; +}
Added: trunk/pretty-printer/src/PrettyPrinter.c =================================================================== --- trunk/pretty-printer/src/PrettyPrinter.c (rev 0) +++ trunk/pretty-printer/src/PrettyPrinter.c 2009-08-09 20:56:04 UTC (rev 881) @@ -0,0 +1,744 @@ +/** + * Written by Cédric Tabin + * http://www.astorm.ch/ + * Version 1.0 - 08.08.2009 + * + * Code under licence GPLv2 + * Geany - http://www.geany.org/ + */ + +#include "PrettyPrinter.h" + +//======================= FUNCTIONS ==================================================================== + +//xml pretty printing functions +static void putCharInBuffer(char charToAdd); //put a char into the new char buffer +static void putCharsInBuffer(char* charsToAdd); //put the chars into the new char buffer +static void putNextCharsInBuffer(int nbChars); //put the next nbChars of the input buffer into the new buffer +static int readWhites(); //read the next whites into the input buffer +static char readNextChar(); //read the next char into the input buffer; +static char getNextChar(); //returns the next char but do not increase the input buffer index (use readNextChar for that) +static char getPreviousInsertedChar(); //returns the last inserted char into the new buffer +static boolean isWhite(char c); //check if the specified char is a white +static boolean isLineBreak(char c); //check if the specified char is a new line +static int putNewLine(); //put a new line into the new char buffer with the correct number of whites (indentation) +static boolean isInlineNodeAllowed(); //check if it is possible to have an inline node +static void resetBackwardIndentation(boolean resetLineBreak); //reset the indentation for the current depth (just reset the index in fact) + +//specific parsing functions +static int processElements(); //returns the number of elements processed +static void processElementAttribute(); //process on attribute of a node +static void processElementAttributes(); //process all the attributes of a node +static void processHeader(); //process the header <?xml version="..." ?> +static void processNode(); //process an XML node +static void processTextNode(); //process a text node +static void processComment(); //process a comment +static void processCDATA(); //process a CDATA node +static void processDoctype(); //process a DOCTYPE node +static void processDoctypeElement(); //process a DOCTYPE ELEMENT node + +//debug function +static void printDebugStatus(); //just print some variables into the console for debugging + +//============================================ PRIVATE PROPERTIES ====================================== + +//those are variables that are shared by the functions and +//shouldn't be altered. + +static int result; //result of the pretty printing +static char* xmlPrettyPrinted; //new buffer for the formatted XML +static int xmlPrettyPrintedLength; //buffer size +static int xmlPrettyPrintedIndex; //buffer index (position of the next char to insert) +static char* inputBuffer; //input buffer +static int inputBufferLength; //input buffer size +static int inputBufferIndex; //input buffer index (position of the next char to read into the input string) +static int currentDepth; //current depth (for indentation) +static char* currentNodeName; //current node name +static boolean appendIndentation; //if the indentation must be added (with a line break before) +static boolean lastNodeOpen; //defines if the last action was a not opening or not +static PrettyPrintingOptions* options; //options of PrettyPrinting + +//============================================ GENERAL FUNCTIONS ======================================= + +int processXMLPrettyPrinting(char** buffer, int* length, PrettyPrintingOptions* ppOptions) +{ + //empty buffer, nothing to process + if (*length == 0) { return PRETTY_PRINTING_EMPTY_XML; } + if (buffer == NULL || *buffer == NULL) { return PRETTY_PRINTING_EMPTY_XML; } + + //initialize the variables + result = PRETTY_PRINTING_SUCCESS; + boolean freeOptions = FALSE; + if (ppOptions == NULL) { ppOptions = createDefaultPrettyPrintingOptions(); freeOptions = TRUE; } + options = ppOptions; + + currentNodeName = NULL; + appendIndentation = FALSE; + lastNodeOpen = FALSE; + xmlPrettyPrintedIndex = 0; + inputBufferIndex = 0; + currentDepth = -1; + + inputBuffer = *buffer; + inputBufferLength = *length; + + xmlPrettyPrintedLength = *length; + xmlPrettyPrinted = (char*)malloc(sizeof(char)*(*length)); + if (xmlPrettyPrinted == NULL) { exit(PRETTY_PRINTING_MALLOC_ERROR); } + + //go to the first char + readWhites(); + + //process the pretty-printing + processElements(); + + //close the buffer + putCharInBuffer('\0'); + + //adjust the final size + xmlPrettyPrinted = realloc(xmlPrettyPrinted, xmlPrettyPrintedIndex); + if (xmlPrettyPrinted == NULL) { exit(PRETTY_PRINTING_MALLOC_ERROR); } + + //freeing the unused values + if (freeOptions) { free(options); } + + //if success, then update the values + if (result == PRETTY_PRINTING_SUCCESS) + { + free(*buffer); + *buffer = xmlPrettyPrinted; + *length = xmlPrettyPrintedIndex-2; //the '\0' is not in the length + } + //else clean the other values + else + { + free(xmlPrettyPrinted); + } + + //updating the pointers for the using into the caller function + xmlPrettyPrinted = NULL; //avoid reference + inputBuffer = NULL; //avoid reference + currentNodeName = NULL; //avoid reference + options = NULL; //avoid reference + + //and finally the result + return result; +} + +PrettyPrintingOptions* createDefaultPrettyPrintingOptions() +{ + PrettyPrintingOptions* options = (PrettyPrintingOptions*)malloc(sizeof(PrettyPrintingOptions)); + if (options == NULL) { fprintf(stderr, "createDefaultPrettyPrintingOptions : Unable to allocate memory"); return NULL; } + + options->indentChar = ' '; + options->indentLength = 2; + options->oneLineText = TRUE; + options->inlineText = TRUE; + options->oneLineComment = TRUE; + options->inlineComment = TRUE; + options->oneLineCdata = TRUE; + options->inlineCdata = TRUE; + options->emptyNodeStripping = TRUE; + options->emptyNodeStrippingSpace = TRUE; + options->forceEmptyNodeSplit = FALSE; + options->trimLeadingWhites = TRUE; + options->trimTrailingWhites = TRUE; + + return options; +} + +void putNextCharsInBuffer(int nbChars) +{ + for (int i=0 ; i<nbChars ; ++i) + { + char c = readNextChar(); + putCharInBuffer(c); + } +} + +void putCharInBuffer(char charToAdd) +{ + //check if the buffer is full and reallocation if needed + if (xmlPrettyPrintedIndex >= xmlPrettyPrintedLength) + { + if (charToAdd == '\0') { ++xmlPrettyPrintedLength; } + else { xmlPrettyPrintedLength += inputBufferLength; } + xmlPrettyPrinted = (char*)realloc(xmlPrettyPrinted, xmlPrettyPrintedLength); + if (xmlPrettyPrinted == NULL) { exit(PRETTY_PRINTING_MALLOC_ERROR); } + } + + //putting the char and increase the index for the next one + xmlPrettyPrinted[xmlPrettyPrintedIndex] = charToAdd; + ++xmlPrettyPrintedIndex; +} + +void putCharsInBuffer(char* charsToAdd) +{ + int index = 0; + while (charsToAdd[index] != '\0') + { + putCharInBuffer(charsToAdd[index]); + ++index; + } +} + +char getPreviousInsertedChar() +{ + return xmlPrettyPrinted[xmlPrettyPrintedIndex-1]; +} + +int putNewLine() +{ + putCharInBuffer('\n'); + int spaces = currentDepth*options->indentLength; + for(int i=0 ; i<spaces ; ++i) + { + putCharInBuffer(options->indentChar); + } + + return spaces; +} + +char getNextChar() +{ + return inputBuffer[inputBufferIndex]; +} + +char readNextChar() +{ + return inputBuffer[inputBufferIndex++]; +} + +int readWhites() +{ + int counter = 0; + while(isWhite(inputBuffer[inputBufferIndex])) + { + ++counter; + ++inputBufferIndex; + } + + return counter; +} + +boolean isWhite(char c) +{ + if (c == ' ') return TRUE; + if (c == '\t') return TRUE; + if (c == '\r') return TRUE; + if (c == '\n') return TRUE; + + return FALSE; +} + +boolean isLineBreak(char c) +{ + if (c == '\n') return TRUE; + if (c == '\r') return TRUE; + + return FALSE; +} + +boolean isInlineNodeAllowed() +{ + //the last action was not an opening => inline not allowed + if (!lastNodeOpen) { return FALSE; } + + int firstChar = getNextChar(); //should be '<' or we are in a text node + int secondChar = inputBuffer[inputBufferIndex+1]; //should be '!' + int thirdChar = inputBuffer[inputBufferIndex+2]; //should be '-' or '[' + + int index = inputBufferIndex+1; + if (firstChar == '<') + { + //another node is being open ==> no inline ! + if (secondChar != '!') { return FALSE; } + + //okay we are in a comment node, so read until it is closed + + //select the closing char + char closingComment = '-'; + if (thirdChar == '[') { closingComment = ']'; } + + //read until closing + char oldChar = ' '; + index += 3; //that by pass meanless chars + boolean loop = TRUE; + while (loop) + { + char current = inputBuffer[index]; + if (current == closingComment && oldChar == closingComment) { loop = FALSE; } //end of comment + oldChar = current; + ++index; + } + + //okay now avoid blanks + // inputBuffer[index] is now '>' + ++index; + while (isWhite(inputBuffer[index])) { ++index; } + } + else + { + //this is a text node. Simply loop to the next '<' + while (inputBuffer[index] != '<') { ++index; } + } + + //check what do we have now + char currentChar = inputBuffer[index]; + if (currentChar == '<') + { + //check if that is a closing node + currentChar = inputBuffer[index+1]; + if (currentChar == '/') + { + //as we are in a correct XML (so far...), if the node is being + //directly close, the inline is allowed !!! + return TRUE; + } + } + + //inline not allowed... + return FALSE; +} + +void resetBackwardIndentation(boolean resetLineBreak) +{ + xmlPrettyPrintedIndex -= (currentDepth*options->indentLength); + if (resetLineBreak) { --xmlPrettyPrintedIndex; } +} + +//######################################################################################################################################### +//----------------------------------------------------------------------------------------------------------------------------------------- + +//----------------------------------------------------------------------------------------------------------------------------------------- +//=============================================================== NODE FUNCTIONS ========================================================== +//----------------------------------------------------------------------------------------------------------------------------------------- + +//----------------------------------------------------------------------------------------------------------------------------------------- +//######################################################################################################################################### + +int processElements() +{ + int counter = 0; + ++currentDepth; + boolean loop = TRUE; + while (loop && result == PRETTY_PRINTING_SUCCESS) + { + //strip unused whites + readWhites(); + + char nextChar = getNextChar(); + if (nextChar == '\0') { return 0; } //no more data to read + + //put a new line with indentation + if (appendIndentation) { putNewLine(); } + + //always append indentation (but need to store the state) + boolean indentBackward = appendIndentation; + appendIndentation = TRUE; + + //okay what do we have now ? + if (nextChar != '<') + { + //a simple text node + processTextNode(); + ++counter; + } + else //some more check are needed + { + nextChar = inputBuffer[inputBufferIndex+1]; + if (nextChar == '!') + { + char oneMore = inputBuffer[inputBufferIndex+2]; + if (oneMore == '-') { processComment(); ++counter; } //a comment + else if (oneMore == '[') { processCDATA(); ++counter; } //cdata + else if (oneMore == 'D') { processDoctype(); ++counter; } //doctype <!DOCTYPE ... > + else if (oneMore == 'E') { processDoctypeElement(); ++counter; } //doctype element <!ELEMENT ... > + else { fprintf(stderr, "processElements : Invalid char '%c' afer '<!'", oneMore); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; } + } + else if (nextChar == '/') + { + //close a node => stop the loop !! + loop = FALSE; + if (indentBackward) { xmlPrettyPrintedIndex -= options->indentLength; } //INDEX HACKING + } + else if (nextChar == '?') + { + //this is a header + processHeader(); + } + else + { + //a new node is open + processNode(); + ++counter; + } + } + } + + --currentDepth; + return counter; +} + +void processElementAttribute() +{ + //process the attribute name + char nextChar = readNextChar(); + while (nextChar != '=') + { + putCharInBuffer(nextChar); + nextChar = readNextChar(); + } + + putCharInBuffer(nextChar); //that's the '=' + + //read the simple quote or double quote and put it into the buffer + char quote = readNextChar(); + putCharInBuffer(quote); + + //process until the last quote + char value = readNextChar(); + while(value != quote) + { + putCharInBuffer(value); + value = readNextChar(); + } + + //simply add the last quote + putCharInBuffer(quote); +} + +void processElementAttributes() +{ + char current = getNextChar(); //should not be a white + if (isWhite(current)) { fprintf(stderr, "processElementAttributes : first char shouldn't be a white"); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + + boolean loop = TRUE; + while (loop) + { + readWhites(); //strip the whites + + char next = getNextChar(); //don't read the last char => will be processed afterwards + if (next == '/') { loop = FALSE; } /* end of node */ + else if (next == '>') { loop = FALSE; } /* end of tag */ + else if (next == '?') { loop = FALSE; } /* end of header */ + else + { + putCharInBuffer(' '); //put only one space to separate attributes + processElementAttribute(); + } + } +} + +void processHeader() +{ + int firstChar = inputBuffer[inputBufferIndex]; //should be '<' + int secondChar = inputBuffer[inputBufferIndex+1]; //must be '?' + + if (firstChar != '<') { fprintf(stderr, "processHeader : first char should be '<' (not '%c')", firstChar); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } /* what ?????? invalid xml !!! */ + if (secondChar == '?') + { + putNextCharsInBuffer(2); //puts the '<' and '?' chars into the new buffer + + while(!isWhite(getNextChar())) { putNextCharsInBuffer(1); } + + readWhites(); + processElementAttributes(); + putNextCharsInBuffer(2); //puts the '?' and '>' chars into the new buffer + } +} + +void processNode() +{ + int opening = readNextChar(); + if (opening != '<') { fprintf(stderr, "processNode : The first char should be '<' (not '%c')", opening); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + putCharInBuffer(opening); + + //read the node name + int nodeNameLength = 0; + while (!isWhite(getNextChar()) && getNextChar() != '>' && getNextChar() != '/') + { + putNextCharsInBuffer(1); + ++nodeNameLength; + } + + //store the name + char* nodeName = (char*)malloc(sizeof(char)*nodeNameLength+1); + if (nodeName == NULL) { exit(PRETTY_PRINTING_MALLOC_ERROR); } + nodeName[nodeNameLength] = '\0'; + for (int i=0 ; i<nodeNameLength ; ++i) + { + int index = xmlPrettyPrintedIndex-nodeNameLength+i; + nodeName[i] = xmlPrettyPrinted[index]; + } + + currentNodeName = nodeName; //set the name for using in other methods + lastNodeOpen = TRUE; + + //process the attributes + readWhites(); + processElementAttributes(); + + //process the end of the tag + int subElementsProcessed = 0; + char nextChar = getNextChar(); //should be either '/' or '>' + if (nextChar == '/') //the node is being closed immediatly + { + //closing node directly + if (options->emptyNodeStripping || !options->forceEmptyNodeSplit) + { + if (options->emptyNodeStrippingSpace) { putCharInBuffer(' '); } + putNextCharsInBuffer(2); + } + //split the closing nodes + else + { + readNextChar(); //removing '/' + readNextChar(); //removing '>' + + putCharInBuffer('>'); + if (!options->inlineText) { putNewLine(); } //no inline text => new line ! + putCharsInBuffer("</"); + putCharsInBuffer(currentNodeName); + putCharInBuffer('>'); + } + + lastNodeOpen=FALSE; + return; + } + else if (nextChar == '>') { putNextCharsInBuffer(1); subElementsProcessed = processElements(TRUE); } //the tag is just closed (maybe some content) + else { fprintf(stderr, "processNode : Invalid character '%c'", nextChar); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + + //if the code reaches this area, then the processElements has been called and we must + //close the opening tag + char closeChar = getNextChar(); + if (closeChar != '<') { fprintf(stderr, "processNode : Invalid character '%c' for closing tag (should be '<')", closeChar); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + + do + { + closeChar = readNextChar(); + putCharInBuffer(closeChar); + } + while(closeChar != '>'); + + //there is no elements + if (subElementsProcessed == 0) + { + //the node will be stripped + if (options->emptyNodeStripping) + { + xmlPrettyPrintedIndex -= nodeNameLength+4; //because we have '<nodeName ...></nodeName>' + resetBackwardIndentation(TRUE); + + if (options->emptyNodeStrippingSpace) { putCharInBuffer(' '); } + putCharsInBuffer("/>"); + } + //the closing tag will be put on the same line + else if (options->inlineText) + { + xmlPrettyPrintedIndex -= nodeNameLength+3; //because we have '</nodeName>' + resetBackwardIndentation(TRUE); + + //rewrite the node name + putCharsInBuffer("</"); + putCharsInBuffer(currentNodeName); + putCharInBuffer('>'); + } + } + + //the node is closed + lastNodeOpen = FALSE; + + //freeeeeeee !!! + free(nodeName); + nodeName = NULL; + currentNodeName = NULL; +} + +void processComment() +{ + boolean inlineAllowed = FALSE; + if (options->inlineComment) { inlineAllowed = isInlineNodeAllowed(); } + if (inlineAllowed) { resetBackwardIndentation(TRUE); } + + putNextCharsInBuffer(4); //add the chars '<!--' + + char oldChar = '-'; + boolean loop = TRUE; + while (loop) + { + char nextChar = readNextChar(); + if (oldChar == '-' && nextChar == '-') //the comment is being closed + { + loop = FALSE; + } + + if (!isLineBreak(nextChar)) //the comment simply continues + { + putCharInBuffer(nextChar); + oldChar = nextChar; + } + else if (!options->oneLineComment) //oh ! there is a line break ! + { + readWhites(); //strip the whites and new line + putNewLine(); //put a new indentation line + oldChar = ' '; //and update the last char + + //TODO manage relative spacing + } + else //the comments must be inlined + { + readWhites(); //strip the whites and add a space if necessary + if (getPreviousInsertedChar() != ' ') { putCharInBuffer(' '); } + } + } + + char lastChar = readNextChar(); //should be '>' + if (lastChar != '>') { fprintf(stderr, "processComment : last char must be '>' (not '%c')", lastChar); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + putCharInBuffer(lastChar); + + if (inlineAllowed) { appendIndentation = FALSE; } + + //there vas no node open + lastNodeOpen = FALSE; +} + +void processTextNode() +{ + //checks if inline is allowed + boolean inlineTextAllowed = FALSE; + if (options->inlineText) { inlineTextAllowed = isInlineNodeAllowed(); } + if (inlineTextAllowed) { resetBackwardIndentation(TRUE); } //remove previous indentation + + //the leading whites are automatically stripped. So we re-add it + if (!options->trimLeadingWhites) + { + int backwardIndex = inputBufferIndex-1; + while (inputBuffer[backwardIndex] == ' ' || inputBuffer[backwardIndex] == '\t') { --backwardIndex; } //backward rolling + + //now the input[backwardIndex] IS NOT a white. So we go to the next char... + ++backwardIndex; + + //and then re-add the whites + while (inputBuffer[backwardIndex] == ' ' || inputBuffer[backwardIndex] == '\t') + { + putCharInBuffer(inputBuffer[backwardIndex]); + ++backwardIndex; + } + } + + //process the text into the node + while(getNextChar() != '<') + { + char nextChar = readNextChar(); + if (isLineBreak(nextChar)) + { + readWhites(); + if (options->oneLineText) + { + //as we can put text on one line, remove the line break and replace it by a space + //but only if the previous char wasn't a space + if (getPreviousInsertedChar() != ' ') { putCharInBuffer(' '); } + } + else + { + //put a new line only if the closing tag is not reached + if (getNextChar() != '<') + { + putNewLine(); + } + } + } + else + { + putCharInBuffer(nextChar); + } + } + + //strip the trailing whites + if (options->trimTrailingWhites) + { + while(getPreviousInsertedChar() == ' ' || getPreviousInsertedChar() == '\t') + { + --xmlPrettyPrintedIndex; + } + } + + //remove the indentation for the closing tag + if (inlineTextAllowed) { appendIndentation = FALSE; } + + //there vas no node open + lastNodeOpen = FALSE; +} + +void processCDATA() +{ + boolean inlineAllowed = FALSE; + if (options->inlineCdata) { inlineAllowed = isInlineNodeAllowed(); } + if (inlineAllowed) { resetBackwardIndentation(TRUE); } + + putNextCharsInBuffer(9); //putting the '<![CDATA[' into the buffer + + boolean loop = TRUE; + char oldChar = '['; + while(loop) + { + char nextChar = readNextChar(); + if (oldChar == ']' && nextChar == ']') { loop = FALSE; } //end of cdata + + if (!isLineBreak(nextChar)) //the cdata simply continues + { + putCharInBuffer(nextChar); + oldChar = nextChar; + } + else if (!options->oneLineCdata) + { + readWhites(); //strip the whites and new line + putNewLine(); //put a new indentation line + oldChar = ' '; //and update the last char + + //TODO manage relative spacing + } + else //cdata are inlined + { + readWhites(); //strip the whites and add a space if necessary + if(getPreviousInsertedChar() != ' ') { putCharInBuffer(' '); } + } + } + + char lastChar = readNextChar(); //should be '>' + if (lastChar != '>') { fprintf(stderr, "processCDATA : last char must be '>' (not '%c')", lastChar); printDebugStatus(); result = PRETTY_PRINTING_INVALID_CHAR_ERROR; return; } + putCharInBuffer(lastChar); + + if (inlineAllowed) { appendIndentation = FALSE; } + + //there vas no node open + lastNodeOpen = FALSE; +} + +void processDoctype() +{ + fprintf(stderr, "DOCTYPE is currently not supported by PrettyPrinter\n"); + fflush(stderr); + + result = PRETTY_PRINTING_NOT_SUPPORTED_YET; +} + +void processDoctypeElement() +{ + fprintf(stderr, "ELEMENT is currently not supported by PrettyPrinter\n"); + fflush(stderr); + + result = PRETTY_PRINTING_NOT_SUPPORTED_YET; +} + +void printDebugStatus() +{ + fprintf(stderr, "\n===== INPUT =====\n%s\n=================\ninputLength = %d\ninputIndex = %d\noutputLength = %d\noutputIndex = %d\n", + inputBuffer, + inputBufferLength, + inputBufferIndex, + xmlPrettyPrintedLength, + xmlPrettyPrintedIndex); + fflush(stderr); +}
Added: trunk/pretty-printer/src/PrettyPrinter.h =================================================================== --- trunk/pretty-printer/src/PrettyPrinter.h (rev 0) +++ trunk/pretty-printer/src/PrettyPrinter.h 2009-08-09 20:56:04 UTC (rev 881) @@ -0,0 +1,55 @@ +/** + * Written by Cédric Tabin + * http://www.astorm.ch/ + * Version 1.0 - 08.08.2009 + * + * Code under licence GPLv2 + * Geany - http://www.geany.org/ + */ + +#ifndef PRETTY_PRINTER_H +#define PRETTY_PRINTER_H + +//========================================== INCLUDES ========================================================== + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +//========================================== DEFINES =========================================================== + +//defines +#define PRETTY_PRINTING_SUCCESS 0 +#define PRETTY_PRINTING_MALLOC_ERROR -1 +#define PRETTY_PRINTING_INVALID_CHAR_ERROR 1 +#define PRETTY_PRINTING_EMPTY_XML 2 +#define PRETTY_PRINTING_NOT_SUPPORTED_YET 3 + +//base type +#define boolean int + +//========================================== STRUCTURES ======================================================= + +typedef struct +{ + char indentChar; //char used for indentation + int indentLength; //number of char to use for indentation (by default 2 spaces) + boolean oneLineText; //text is put on one line + boolean inlineText; //if possible text are inline (no return after the opening node and before closing node) + boolean oneLineComment; //comments are put on one line + boolean inlineComment; //if possible comments are inline (no return after the opening node and before closing node) + boolean oneLineCdata; //cdata are put on one line + boolean inlineCdata; //if possible cdata are inline (no return after the opening node and before closing node) + boolean emptyNodeStripping; //the empty nodes such <node></node> are set to <node/> + boolean emptyNodeStrippingSpace; //put a space before the '/>' when a node is stripped + boolean forceEmptyNodeSplit; //force an empty node to be splitted : <node /> becomes <node></node> (only if emptyNodeStripping = false) + boolean trimLeadingWhites; //trim the leading whites in a text node + boolean trimTrailingWhites; //trim the trailing whites in a text node +} PrettyPrintingOptions; + +//========================================== FUNCTIONS ========================================================= + +int processXMLPrettyPrinting(char** xml, int* length, PrettyPrintingOptions* ppOptions); //process the pretty-printing on a valid xml string (no check done !!!). The ppOptions ARE NOT FREE-ED after processing. The method returns 0 if the pretty-printing has been done. +PrettyPrintingOptions* createDefaultPrettyPrintingOptions(); //creates a default PrettyPrintingOptions object + +#endif
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
plugins-commits@lists.geany.org