#include "precompiled.h" /* * wxJavaScript - textfile.cpp * * Copyright (c) 2002-2007 Franky Braem and the wxJavaScript project * * Project Info: http://www.wxjavascript.net or http://wxjs.sourceforge.net * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * $Id: textfile.cpp 598 2007-03-07 20:13:28Z fbraem $ */ #include #ifndef WX_PRECOMP #include #endif #include "../common/main.h" #include "../common/index.h" #include "../ext/wxjs_ext.h" #include "textfile.h" #include "textline.h" using namespace wxjs; using namespace wxjs::io; /*** * textfile * io * * The wxTextFile is a simple class which allows to work with text files on line by line basis. * It also understands the differences in line termination characters under different platforms * and will not do anything bad to files with "non native" line termination sequences - in fact, * it can be also used to modify the text files and change the line termination characters from * one type (say DOS) to another (say Unix). *

* One word of warning: the class is not at all optimized for big files and thus it will load the * file entirely into memory when opened. Of course, you should not work in this way with large files * (as an estimation, anything over 1 Megabyte is surely too big for this class). On the other hand, * it is not a serious limitation for small files like configuration files or program sources which * are well handled by wxTextFile. *

* The typical things you may do with wxTextFile in order are: *
    *
  • * Create and open it: this is done with either @wxTextFile#create or @wxTextFile#open function * which opens the file (name may be specified either as the argument to these functions or in * the constructor), reads its contents in memory (in the case of open) and closes it. *
  • *
  • * Work with the lines in the file: this may be done either with "direct access" properties * like @wxTextFile#lines or with "sequential access" properties which include * @wxTextFile#firstLine/@wxTextFile#nextLine and also @wxTextFile#lastLine/@wxTextFile#prevLine. * For the sequential access properties the current line number is maintained: @wxTextFile#currentLine * and may be changed with @wxTextFile#goToLine. *
  • *
  • * Add/remove lines to the file: @wxTextFile#addLine and @wxTextFile#insertLine add new lines while * @wxTextFile#removeLine deletes the existing ones. @wxTextFile#clear resets the file to empty. *
  • *
  • * Save your changes: notice that the changes you make to the file will not be saved automatically; * calling @wxTextFile#close or doing nothing discards them! To save the changes you must explicitly * call @wxTextFile#write - here, you may also change the line termination type if you wish *
  • *
* Remark :wxJS adds the @wxTextLine class. In wxWidgets, lines can be changed because * most methods return a reference. This is not possible in JavaScript, but this can be solved by * returning an object: @wxTextLine. This class can be used to access the lines like they were * an array and you can change them directly: *

 *   var file = new wxTextFile("text.txt");
 *   file.line[0] = "This is the first line";
 *  
*
*/ WXJS_INIT_CLASS(TextFile, "wxTextFile", 0) /*** * * * * * * * * */ void TextFile::InitClass(JSContext *cx, JSObject *obj, JSObject *proto) { JSConstDoubleSpec wxTextFileType[] = { WXJS_CONSTANT(wxTextFileType_, None) WXJS_CONSTANT(wxTextFileType_, Unix) WXJS_CONSTANT(wxTextFileType_, Dos) WXJS_CONSTANT(wxTextFileType_, Mac) { 0 } }; JSObject *constObj = JS_DefineObject(cx, obj, "wxTextFileType", NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineConstDoubles(cx, constObj, wxTextFileType); } /*** * * * * The filename * * * Constructs a new wxTextFile object. The file is not loaded * into memory. Use @wxTextFile#open to do it. * * */ wxTextFile *TextFile::Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, bool constructing) { wxString fileName; switch(argc) { case 1: { FromJS(cx, argv[0], fileName); wxTextFile *file = new wxTextFile(fileName); return file; } case 0: return new wxTextFile(); } return NULL; } /*** * * * Returns the current line: it has meaning only when you're using * @wxTextFile#firstLine/@wxTextFile#nextLine properties, it doesn't get updated when you're * using "direct access" property @wxTextFile#lines. @wxTextFile#firstLine and @wxTextFile#lastLine * also change the value of the current line, as well as @wxTextFile#goToLine. * * * Returns true when end of file is reached. * When the file is not open, undefined is returned. * * * Get the first line. * * * Guess the type of file (which is supposed to be opened). If sufficiently many lines of the file * are in DOS/Unix/Mac format, the corresponding value will be returned. If the detection mechanism fails wxTextFileType_None is returned * * * Is the file open? * * * Get the last line * * * Number of lines in the file. * * * Get an array of lines * * * Get the nextline * * * Get the previous line * * */ WXJS_BEGIN_PROPERTY_MAP(TextFile) WXJS_READONLY_PROPERTY(P_CURRENT_LINE, "currentLine") WXJS_READONLY_PROPERTY(P_EOF, "eof") WXJS_READONLY_PROPERTY(P_FIRST_LINE, "firstLine") WXJS_READONLY_PROPERTY(P_LAST_LINE, "lastLine") WXJS_READONLY_PROPERTY(P_NEXT_LINE, "nextLine") WXJS_READONLY_PROPERTY(P_PREV_LINE, "prevLine") WXJS_READONLY_PROPERTY(P_OPENED, "opened") WXJS_READONLY_PROPERTY(P_LINE_COUNT, "lineCount") WXJS_READONLY_PROPERTY(P_NAME, "name") WXJS_READONLY_PROPERTY(P_GUESS_TYPE, "guessType") WXJS_READONLY_PROPERTY(P_LINES, "lines") WXJS_END_PROPERTY_MAP() bool TextFile::GetProperty(wxTextFile *p, JSContext *cx, JSObject *obj, int id, jsval *vp) { switch(id) { case P_EOF: *vp = p->IsOpened() ? ToJS(cx, p->Eof()) : JSVAL_VOID; break; case P_OPENED: *vp = ToJS(cx, p->IsOpened()); break; case P_LINE_COUNT: *vp = ToJS(cx, p->GetLineCount()); break; case P_CURRENT_LINE: *vp = ToJS(cx, p->GetCurrentLine()); break; case P_FIRST_LINE: p->GetFirstLine(); *vp = TextLine::CreateObject(cx, new Index(0), obj); break; case P_NEXT_LINE: if ( ! p->Eof() ) { p->GetNextLine(); *vp = TextLine::CreateObject(cx, new Index(p->GetCurrentLine()), obj); } else { *vp = JSVAL_VOID; } break; case P_PREV_LINE: if ( p->GetCurrentLine() == 0 ) { *vp = JSVAL_VOID; } else { p->GetPrevLine(); *vp = TextLine::CreateObject(cx, new Index(p->GetCurrentLine()), obj); } break; case P_LAST_LINE: p->GetLastLine(); *vp = TextLine::CreateObject(cx, new Index(p->GetCurrentLine()), obj); break; case P_GUESS_TYPE: *vp = ToJS(cx, (int) p->GuessType()); break; case P_LINES: *vp = TextLine::CreateObject(cx, new Index(0), obj); break; case P_NAME: *vp = ToJS(cx, wxString(p->GetName())); break; } return true; } WXJS_BEGIN_METHOD_MAP(TextFile) WXJS_METHOD("addLine", addLine, 1) WXJS_METHOD("close", close, 0) WXJS_METHOD("clear", clear, 0) WXJS_METHOD("create", create, 1) WXJS_METHOD("exists", exists, 0) WXJS_METHOD("gotoLine", gotoLine, 1) WXJS_METHOD("open", open, 2) WXJS_METHOD("insertLine", insertLine, 2) WXJS_METHOD("removeLine", removeLine, 1) WXJS_METHOD("write", write, 2) WXJS_END_METHOD_MAP() /*** * * * * The line to add * * * * * Adds a line to the end of file. When Type is omitted * the native default is used. * * */ JSBool TextFile::addLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); wxString line; FromJS(cx, argv[0], line); if ( argc == 1 ) { p->AddLine(line); return JS_TRUE; } else { int type; if ( FromJS(cx, argv[1], type) ) { p->AddLine(line, (wxTextFileType) type); return JS_TRUE; } } return JS_FALSE; } /*** * * * * Removes all lines * * */ JSBool TextFile::clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); p->Clear(); return JS_TRUE; } /*** * * * * Closes the file and frees memory, losing all changes. Use @wxTextFile#write if you want to save them. * * */ JSBool TextFile::close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxFile")); *rval = ToJS(cx, p->Close()); return JS_TRUE; } /*** * * * * The name of the file to create. * * * Creates the file with the given name or the name which was given in the constructor. * The array of file lines is initially empty. * It will fail if the file already exists, @wxTextFile#open should be used in this case. * * */ JSBool TextFile::create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); if ( argc != 0 ) { wxString fileName; FromJS(cx, argv[0], fileName); *rval = ToJS(cx, p->Create(fileName)); return JS_TRUE; } *rval = ToJS(cx, p->Create()); return JS_TRUE; } /*** * * * * Return true if file exists - the name of the file should have been specified in the * constructor before calling exists(). * * */ JSBool TextFile::exists(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); *rval = ToJS(cx, p->Exists()); return JS_TRUE; } /*** * * * * * * Changes the value returned by @wxTextFile#currentLine. * * */ JSBool TextFile::gotoLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); int line; if ( FromJS(cx, argv[0], line) ) { p->GoToLine(line); return JS_TRUE; } return JS_FALSE; } /*** * * * The line to insert (without the end-of-line character(s)). * The line position * * * * Insert a line before the line number Pos. * * */ JSBool TextFile::insertLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); wxString line; int pos; FromJS(cx, argv[0], line); if ( ! FromJS(cx, argv[1], pos) ) return JS_FALSE; if ( argc > 2 ) { int type; if ( ! FromJS(cx, argv[2], type) ) return JS_FALSE; p->InsertLine(line, pos, (wxTextFileType) type); } else { p->InsertLine(line, pos); } return JS_TRUE; } /*** * * * * * The name of the file to open * * * Opens the file with the given name or the name which was given in the constructor * and also loads file in memory on success. It will fail if the file does not exist, * @wxTextFile#create should be used in this case. When no encoding is specified * the file is opened in UTF-8. * * */ JSBool TextFile::open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); if ( p->IsOpened() ) return JS_TRUE; if ( argc == 0 ) { *rval = ToJS(cx, p->Open()); return JS_TRUE; } wxString encoding(wxJS_EXTERNAL_ENCODING); FromJS(cx, argv[0], encoding); wxCSConv conv(encoding); bool ok = false; if ( argc > 1 ) { wxString fileName; FromJS(cx, argv[1], fileName); ok = p->Open(fileName, conv); } else { ok = p->Open(conv); } if ( ok ) { // Convert all lines, because internally we want another encoding if ( ! encoding.IsSameAs(wxJS_INTERNAL_ENCODING, false) ) { wxCSConv internalConv(wxJS_INTERNAL_ENCODING); for (wxString str = p->GetFirstLine(); !p->Eof(); str = p->GetNextLine() ) { str = wxString(str.mb_str(conv), internalConv); } p->GoToLine(0); } } *rval = ToJS(cx, ok); return JS_TRUE; } /*** * * * The line to remove. * * * Removes the line from the file * * */ JSBool TextFile::removeLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxFile")); int line; if ( ! FromJS(cx, argv[0], line) ) { return JS_FALSE; } p->RemoveLine(line); return JS_TRUE; } /*** * * * * * * * Change the file on disk. The Type parameter allows you to change the file format * (default argument means "don't change type") and may be used to convert, for example, * DOS files to Unix. * * */ JSBool TextFile::write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { wxTextFile *p = GetPrivate(cx, obj); wxASSERT_MSG(p != NULL, wxT("No private data associated with wxTextFile")); int type = wxTextFileType_None; if ( argc > 0 ) { if ( ! FromJS(cx, argv[0], type) ) return JS_FALSE; } wxString encoding(wxJS_EXTERNAL_ENCODING); if ( argc > 1 ) { FromJS(cx, argv[1], encoding); } wxCSConv conv(encoding); *rval = ToJS(cx, p->Write((wxTextFileType) type, conv)); return JS_TRUE; } WXJS_BEGIN_STATIC_METHOD_MAP(TextFile) WXJS_METHOD("getEOL", getEOL, 1) WXJS_END_METHOD_MAP() /*** * * * * * * Get the line termination string corresponding to given constant. * typeDefault is the value defined during the compilation and corresponds to the * native format of the platform, i.e. it will be wxTextFileType.Dos under Windows, * wxTextFileType.Unix under Unix (including Mac OS X when compiling with the Apple * Developer Tools) and wxTextFileType.Mac under Mac OS * (including Mac OS X when compiling with CodeWarrior). * * */ JSBool TextFile::getEOL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { int type; if ( ! FromJS(cx, argv[0], type) ) return JS_FALSE; *rval = ToJS(cx, wxString(wxTextFile::GetEOL((wxTextFileType) type))); return JS_TRUE; }