Wednesday, April 17, 2013

Installing XDebug on Ubuntu 12.04

mkdir ~/opt && cd ~/opt
wget http://xdebug.org/files/xdebug-2.2.2.tgz
tar -xzvf xdebug-2.2.2.tgz && cd xdebug-2.2.2/
phpize
auto-apt run ./configure
make
sudo checkinstall
sudo nano /etc/php5/mods-available/xdebug.ini
sudo php5enmod xdebug
php -i | grep xdebug

Explanation

  1. Create directory opt inside the home directory and change to created directory. Change directory name and location as needed
  2. Get the latest version of Xdebug. Adjust URL to point to the latest or desired Xdebug version
  3. Prepare the build environment for a PHP extension. See phpize manual.
  4. Use auto-apt just in case there are missing dependencies. Not likely to happen in this case.
  5. Build extension
  6. Create and install deb package. For details see Checkinstall wiki.
    1. The package documentation directory ./doc-pak does not exist.
      1. Should I create a default set of package docs?  [y]: 
      2. Type y
    2. Please write a description for the package.
      1. End your description with an empty line or EOF.
      2. Enter Xdebug 2.2.2 or whatever might be suitable
    3. This package will be built according to these values:
      1. Options 0 to 13 listed
      2. Enter a number to change any of them or press ENTER to continue:
      3. Make changes as needed
  7. Create extension configuration file. For details see Xdebug Documentation.
  8. Enable extension
  9. Check if extension is installed and configured

Basic extension configuration

zend_extension=/usr/lib/php5/20100525/xdebug.so

Prerequests

See Compiling Software wiki entry. Aside from basics php5-dev package must be installed on the system.

Thursday, November 8, 2012

NetBeans IDE 7.3 Beta 2 - Find Usages

NetBeans 7.3 Beta 2 has been released earlier today. You will find more information here: NB 7.3 beta 2 Release Information

One of the most usable improvements I found is the way how results are displayed/ordered in Find Usages Logical View.

In earlier versions it was quite hard to find what are you looking for. Attempting to get usages of a method having common name such as init(), setHandler() or run() might return hundreds of results. The only way to figure out a location of a file where usage occurred was to place a mouse over a file name and wait for a tooltip to display.



Find Usages - Logical View is way better organized in NetBeans IDE 7.3 Beta 2.



One can easily disregard usages of run() method in EZComponents or Zend Framework and focus on desired components.

Hopefully this change will find it's way to Refactoring - Logical View in final NetBeans IDE 7.3 release.

Wednesday, July 25, 2012

NetCAT

I am astonished!  Over 40 issues I reported during NetCAT 7.2 where resolved and included in the latest NetBeans 7.2 release!

In total NetCAT team reported 1019 bugs of which 37% was resolved. Awesome work done by the NetBeans developers and QE engineers.


184465Opening of npss file may block AWT
184669java.util.MissingResourceException: Can't find resource for bundle org.openide.util.NbBundle$PBundle, key org.openide.actions.PopupAction
201257InternalError: processing event: -1
203427[71cat] IndexOutOfBoundsException: Invalid line index=600 >= lineCount=1
203827Minimize TextLayout creation and processing to improve performance
2048184s - ToolbarPool$Folder.createInstance may block in waitFinished()
207247[71cat] NullPointerException at org.netbeans.modules.php.editor.nav.DeclarationFinderImpl.getReferenceSpan
207402[71cat] IllegalArgumentException: Parameter file was not normalized. Was /.. instead of /
207991Large project hangs Netbeans 7.1 indefinitely.
208322[71cat] NullPointerException at org.netbeans.modules.versioning.core.api.VCSFileProxy.createFileProxy
208623[71cat] NullPointerException at org.netbeans.modules.versioning.DelegatingVCS$DelegatingHistoryProvider.getHistory
208652Infinite loop in DirectMergeContainer
208774IllegalArgumentException: Comparison method violates its general contract!
208788IllegalStateException: startCreationOffset=694 > matchOffset=683
208931Error when changing text.
209019StringIndexOutOfBoundsException: String index out of range: 0
209327ClassCastException: javax.swing.JLabel cannot be cast to javax.swing.JMenuItem
209460NullPointerException at com.mysql.jdbc.ConnectionImpl.registerStatement
209556NullPointerException at org.netbeans.modules.subversion.ui.repository.Repository.onSelectedRepositoryChange
209594UnsupportedOperationException: Not supported yet.
209665NullPointerException at org.netbeans.modules.bugtracking.spi.BugtrackingFactory.getIssueImpl
209670NullPointerException at javax.swing.BoxLayout.preferredLayoutSize
210058NullPointerException at org.netbeans.modules.subversion.ui.commit.CommitAction$5.getCopiedFromFile
210415NullPointerException at org.netbeans.modules.editor.lib2.view.DocumentView.updateStartEndOffsets
210502AssertionError: profile
210596NullPointerException at org.tigris.subversion.svnclientadapter.javahl.AbstractJhlClientAdapter.getInfo
210628java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification
210767StringIndexOutOfBoundsException: String index out of range: 803
210976StringIndexOutOfBoundsException: String index out of range: 399
211002IllegalArgumentException: Parameter file was not normalized. Was ../../../../usr/share/php/PHPUnit instead of /usr/share/php/PHPUnit
211425java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification
211439Throwable: Using stale writer, possibly forgotten call to store, old owner Thread Thread[RepositoryUpdater.worker,1,system], new owner Thread Thread[AWT-EventQueue-0,6,IDE Main] .
211744IllegalStateException: No instance data can be specified.
211759AssertionError at org.netbeans.modules.csl.editor.hyperlink.GoToSupport.getIdentifierSpan
212120Cannot merge changes from the trunk into working copy
212237IllegalStateException: Somebody changed internal state meanwhile! Expected: Inited: false Thread: Thread[org.netbeans.modules.project.ui.ProjectTab,1,system] InProgress: true MustNotify: false Entries
212246IllegalStateException: Somebody changed internal state meanwhile! Expected: Inited: true Thread: Thread[AWT-EventQueue-0,6,IDE Main] InProgress: true MustNotify: false Entries: 0; vis. entries: 0; Inf
212278ClassCastException: [Lorg.openide.nodes.Node; cannot be cast to [Lorg.netbeans.modules.csl.navigation.ElementNode;
212431IllegalStateException: Problem in some module which uses Window System: Window System API is required to be called from AWT thread only, seehttp://core.netbeans.org/proposals/threading/
213434NullPointerException at org.netbeans.modules.html.editor.completion.HtmlCompletionProvider$DocQuery.doQuery
213441NullPointerException at org.netbeans.modules.search.ui.MatchingObjectNode.setInvalidOriginal


The NetBeans 7.2 is available for download here.

Thursday, January 19, 2012

The NetBeans IDE: PHP 5.4 Support: Array Dereferencing

PHP 5.4 support is available in the NetBeans IDE development builds.

To get PHP 5.4 features available in the existing project go to Project Properties -> Sources -> PHP Version and select PHP 5.4. When creating a new project using New Project Wizard select PHP version from the drop-down menu on the second screen Name and Location.



You can find more on array dereferencing in RFC on PHP Wiki. 

class MyCollection
{

    public function doSomething()
    {
        
    }

}

class ArrayDereferencing
{

    /**
     *
     * @var MyCollection[]
     */
    private $myCollection = array();

    /**
     *
     * @return MyCollection[]
     */
    public function getCollection()
    {
        return $this->myCollection;
    }

    public function getElementAndDoSomething($index)
    {
        return $this->getCollection()[$index]->doSomething();
    }

}
Code completion in the NetBeans PHP Editor:

Ondrej Brejla in the article PHP 5.4 Support: Array Dereferencing on Oracle blog called user to test new PHP 5.4 features and file (component PHP, subcomponent Editor) any issues found.

Wednesday, June 1, 2011

Wish List Sample Application:: PDO Flavor

After Jeffrey Rubinoff asked community for help on rewriting sample wish list application accompanying his tutorial Creating a Database Driven Application With NetBeans IDE PHP Editor number of users raised question why PDO was not used instead of mysqli and OCI8.

During the NetCat 7.0 I had a chance to review Jeff's tutorial. I could not resist to try installing PDO_OCI. Having everything needed running on my Ubuntu box, I was finally able to do what I promised: Create PDO database class. The key objective was to make it easy to switch between MySQL and Oracle XE, preferably just by changing connection string.

Problems

Before porting WishDB class to PDO due to differences between Oracle XE and MySQL queries needed to be normalized. There are two differences affecting Wish List Sample Application:

  • Oracle is case insensitive to object names, and Oracle schema object names are stored as uppercase
  • There is a difference in handling dates between MySQL and Oracle XE
Further on, in few cases database specific PHP functions where used outside WishDB class. Such usage is hardening code maintenance and porting code to PDO. Therefore those cases had to be resolved as well before porting WishDB class.
Finally, if data could be retrieved in a single query don't query database multiple times. Establishing connection with database is bottleneck in every PHP database driven application.

Restructuring the Code

Here is an example:
// MySQL version
        $wisher = $this->query("SELECT id FROM wishers WHERE name = '"
                        . $name . "'");

        // OCI8 version
        $query = "SELECT ID FROM wishers WHERE name = :user_bv";

        // PDO version
        $query = "
            SELECT id ID
            FROM wishers
            WHERE name = :user_bv
            ";
Schema object names in MySQL version of the application are written lowercase. Oracle XE will not have a problem with lowercase names but returned array keys will be uppercase. I decided to add uppercase column names alias and use OCI8 version of the application as base for refactoring. In this way there is no need to change MySQL schema nor PHP code other then WishDB class.

Handling dates is a bit trickier. Both MySQL and Oracle XE store dates internally as timestamp. However, input and output need to be formatted differently. Logical step was to create functions having same names, input parameters and outputting identically formatted date string.

Before creating SQL functions I had to normalize function format_date_for_sql first. I prefer using DateTime class over native PHP functions.
// MySQL version
    function format_date_for_sql($date) {
        if ($date == "")
            return null;
        else {
            $dateParts = date_parse($date);
            return $dateParts['year'] * 10000 + $dateParts['month'] * 100 + $dateParts['day'];
        }
    }

    // OCI8 version
    function format_date_for_sql($date) {
        if ($date == "")
            return null;
        else {
            $dateParts = date_parse($date);
            return $dateParts['year'] * 10000 + '-' + $dateParts['month'] * 100 + '-' + $dateParts['day'];
        }
    }

    // PDO version
    function format_date_for_sql($date)
    {
        if ($date == "") {
            return null;
        } else {
            $dateTime = new DateTime($date, new DateTimeZone("UTC"));
            return $dateTime->format("Y-n-j H:i:s e");
        }
    }


Here are those two SQL functions:

// MySQL version
CREATE DEFINER=`root`@`localhost` FUNCTION  `wishlist`.`format_due_date`(
`in_date` VARCHAR(255) CHARSET latin1
) RETURNS varchar(255) CHARSET latin1
    SQL SECURITY INVOKER
BEGIN
return CONCAT(in_date, SPACE(1), 'UTC');
END $$

CREATE DEFINER=`root`@`localhost` FUNCTION  `wishlist`.`set_due_date`(
`in_date` VARCHAR(255) CHARSET latin1
) RETURNS varchar(255) CHARSET latin1
    SQL SECURITY INVOKER
BEGIN
return SUBSTR(in_date, 1, length(in_date) - 4);
END $$

// Oracle XE version
CREATE OR REPLACE
FUNCTION "FORMAT_DUE_DATE" (in_date in TIMESTAMP) return VARCHAR2 is
begin
return TO_CHAR(IN_DATE, 'YYYY-MM-DD HH24:MI:SS TZR');
end; /

CREATE OR REPLACE
FUNCTION "SET_DUE_DATE" (in_date in VARCHAR2) return VARCHAR2 is
begin return TO_TIMESTAMP_TZ(in_date, 'YYYY-MM-DD HH24:MI:SS TZR');
end; /
Finally queries could be normalized.
// MySQL version
        $this->query("INSERT INTO wishes (wisher_id, description, due_date)" .
                " VALUES (" . $wisherID . ", '" . $description . "', "
                . $this->format_date_for_sql($duedate) . ")");

        // OCI8 version
        $query = "INSERT INTO wishes (wisher_id, description, due_date) VALUES (:wisher_id_bv, :desc_bv, to_date(:due_date_bv, 'YYYY-MM-DD'))";

        // PDO version
        $query = "
            INSERT INTO wishes (wisher_id, description, due_date)
            VALUES (
                :wisher_id_bv,
                :desc_bv,
                set_due_date(:due_date_bv)
                )
            ";
User's wish list is retrieved in two steps: First, a user #id is found by user name and later on list of wishes is found by a user #id.
$wisherID = WishDB::getInstance()->get_wisher_id_by_name($_SESSION['user']);
                $stid = WishDB::getInstance()->get_wishes_by_wisher_id($wisherID);
User's wish list could be retrieved in a single query. Oracle XE supports full outer join but, unfortunately, MySQL does not. Therefore, a query has to be a bit more complex.
SELECT w.id ID, w.description DESCRIPTION,
            format_due_date(w.due_date) DUE_DATE, wr.id WRID
            FROM wishes w RIGHT OUTER JOIN wishers wr
            ON wr.id = w.wisher_id
            WHERE wr.name = :user_bv
Finally, I replaced function get_wishes_by_wisher_id with function get_wishes_by_wisher_name. 
/**
     * Gets user's wishes for the user having given name
     *
     * @param string $name
     * @return ArrayIterator
     */
    public function get_wishes_by_wisher_name($name)
    {
        $query = "";
        $stid = null;
        $row = array();
        $result = null;

        $query = "
            SELECT w.id ID, w.description DESCRIPTION,
            format_due_date(w.due_date) DUE_DATE, wr.id WRID
            FROM wishes w RIGHT OUTER JOIN wishers wr
            ON wr.id = w.wisher_id
            WHERE wr.name = :user_bv
            ";

        $stid = $this->con->prepare($query);
        $stid->bindParam(":user_bv", $name, PDO::PARAM_STR);
        $stid->execute();

        $result = new ArrayIterator();
        while ($row = $stid->fetch(PDO::FETCH_ASSOC)) {
            $result->append($row);
        }
        $result->rewind();

        return $result;
    }
Function get_wishes_by_wisher_name is returning ArrayIterator instead of resultset, letting me to use the same code regardless of database sitting in backend and having more clear code when displaying results.
<table class="std">
            <tr>
                <th>Item</th>
                <th>Due Date</th>
                <th colspan="2">&nbsp;</th>
            </tr>
            <?php
            $wishes = WishDB::getInstance()->
                    get_wishes_by_wisher_name($_SESSION["user"]);
            while ($wishes->valid()):
                $row = $wishes->current();
                $date = new DateTime($row['DUE_DATE'], new DateTimeZone("UTC"));
                if (true === is_null($row["ID"])) {
                    $wishes->next();
                    continue;
                }
                ?>
                <tr>
                    <td>&nbsp;
                        <?php
                        echo htmlentities($row['DESCRIPTION']);
                        ?>
                    </td>
                    <td>&nbsp;
                        <?php
                        echo (is_null($row['DUE_DATE']) ?
                                "" : $date->format("Y, M jS"));
                        $wishID = $row['ID'];
                        ?>
                    </td>
                    <td>
                        <form name="editWish"
                              action="editWish.php" method="GET">
                            <input type="hidden"
                                   name="wishID"
                                   value="<?php echo $wishID; ?>" />
                            <input type="submit" name="editWish" value="Edit" />
                        </form>
                    </td>
                    <td>
                        <form name="deleteWish"
                              action="deleteWish.php" method="POST">
                            <input type="hidden"
                                   name="wishID"
                                   value="<?php echo $wishID; ?>" />
                            <input type="submit"
                                   name="deleteWish"
                                   value="Delete" />
                        </form>
                    </td>
                </tr>
                <?php
                $wishes->next();
            endwhile;
            ?>
        </table>
You can grab the code from Kenai repository.

There is more to be done, for example all input variables must be filtered. You are warmly welcomed to join Wish List Sample Application project.

Saturday, April 9, 2011

NetBeans IDE 7.0 Release Candidate 2 Available for Download

The NetBeans Team announced that the second release candidate build of NetBeans IDE 7.0 is available for download.

Join on Tuesday, April 12th at 6PM GMT, NetBeans Engineering Manager John Jullion-Ceccarelli for a LIVE Oracle TechCast about NetBeans IDE 7.0, which introduces language support for Java SE 7 and provides other feature enhancements. 

There'll be a Q&A segment where viewers can pose questions. Questions can also be posted to Twitter with the hashtag #techcastlive.

The final release of NetBeans IDE 7.0 is planned for April.

Tuesday, April 5, 2011

Installing PDO_OCI on Ubuntu 10.10 x64

The PHP Data Objects (PDO) extension defines a lightweight, consistent interface for accessing databases in PHP.
The PDO provides a data-access abstraction layer that can be used with a variety of databases, giving the flexibility of changing the database backend, without having to alter access methods. Even when using a single RDBMS, the PDO can provide advantages, for example, if using the MySQL, the same data-access methods can be used regardless of the MySQL version.
I reviewed recently the chapter Lesson 1b: Creating Oracle Database Tables from Jeffrey Rubinoff's tutorial Creating a Database Driven Application With PHP, as part of the NetBeans NetCAT 7.0 program. To meat requirements I had to install Oracle XE, Instant Client and the PHP OCI8 extension.
I fancy PDO over all other options. Having Oracle XE running on my HP was a chance to try and test PDO_OCI driver.

If running 32bit Ubuntu read no more: All you need is The Underground PHP and Oracle Manual.

Getting PDO_OCI driver source code and patching conifg.m4

Grab the current PHP snapshot. A patch to PDO_OCI config.m4 for 64bit RPM support was merged on March 30th. See the Bug #44989. Over the time this change will be integrated and available in the downloads section. 
Extract from the downloaded archive the pdo_oci folder residing in the ext folder. 
Open config.m4 located in the extracted pdo_oci folder. 
If using the NetBeans IDE, ctrl+3 to open Favorites, browse to the extracted pdo_oci folder, right click on config.m4 and in the context menu click Open.


Verify that the date in the SVN $Id on the line one is equal or greater then 2011-03-29.
dnl $Id: config.m4 309818 2011-03-29 18:40:20Z felipe $
Download, save and apply patch config.m4.deb.patch attached to the Bug #54451. 
If using the NetBeans IDE, after downloading the patch, make sure that focus is on previously opened config.m4, click Apply Diff Patch in Tools menu, browse to the downloaded patch, select it and click Patch.
Say yes when asked to view applied changes and verify that config.m4 is patched correctly.
Move the folder pdo_oci containing patched config.m4 to /usr/local/src. 
If using the NetBeans IDE open Terminal from Window > Output > Terminal and type:
$ sudo mv ./wherever/is/pdo_oci /usr/local/src

Getting Oracle Instant Client and additional header files

Download from the Instant Client Downloads for Linux x86-64 page instantclient-basiclite-linux-x86-64-11.2.0.2.0.zip and instantclient-sdk-linux-x86-64-11.2.0.2.0.zip.
Extract content of those two archives to /opt/oracle.
If using the NetBeans IDE open Terminal from Window > Output > Terminal and type:
$ sudo unzip ./wherever/is/instantclient-basic-linux-x86-64-11.2.0.2.0.zip -d /opt/oracle
$ sudo unzip ./wherever/is/instantclient-sdk-linux-x86-64-11.2.0.2.0 -d /opt/oracle
# make symbolic links
$ sudo ln -s /opt/oracle/instantclient_11_2/libclntsh.so.11.1 /opt/oracle/instantclient_11_2/libclntsh.so
$ sudo ln -s /opt/oracle/instantclient_11_2/libocci.so.11.1 /opt/oracle/instantclient_11_2/libocci.so
Create new file oracleinstantclient.conf in /etc/ld.so.conf.d, add line /opt/oracle/instantclient_11_2, save, close and run ldconfig.
sudo nano /etc/ld.so.conf.d/oci8.conf
sudo ldconfig

make and make install

If using the NetBeans IDE open Terminal from Window > Output > Terminal and type:
$ cd /usr/local/src/pdo_oci/
$ phpize
$ ./configure
$ make
$ make install
Don't run make test. All test will fail, since PDO extension is not loaded in generated php.ini. 
Verify that pdo_oci.so is in your extension directory. Default path is /usr/lib/php5/20090626/pdo_oci.so.

Load PDO_OCI extension and restart Apache

Create new file pdo_oci.ini in /etc/php/apache2/conf.d, add line extension=pdo_oci.so, save close and restart Apache.
$ sudo nano /etc/php5/apache2/conf.d/pdo_oci8.ini
$ sudo /etc/init.d/apache2 restart

If the NetBeans IDE user grab database structure and insert records as described in the chapter Lesson 1b: Creating Oracle Database Tables from Jeffrey Rubinoff's tutorial Creating a Database Driven Application With PHP. Create new PdoOciTest.php file in the web root of the text application.

<html>
    <head>
        <title>PDO OCI Test Page</title>
    </head>

    <body>
        <?php
        $pdo = new PDO(
                        'oci:dbname=localhost/XE',
                        'jim',
                        'mypassword'
        );
        $sql = "
            SELECT DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID, STREET_ADDRESS,
            POSTAL_CODE, CITY, STATE_PROVINCE
            FROM departments NATURAL JOIN locations
            ORDER by DEPARTMENT_NAME
            ";
        $stmt = $pdo->query($sql);
        $result = $stmt->fetchAll();
        ?>
        <table>
            <?php
            foreach ($result as $row) {
                ?><tr><?php
                foreach ($row as $item) {
                    ?><td><?php
                    echo ($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;");
                    ?></td><?php
                }
                ?></tr><?php
            }
            ?>
        </table>
    </body>
</html>
Having focus on the created file go to Run > Run File or press space+F6. The file will run on the defined local web server and open in the default browser,

Saturday, April 2, 2011

Eidos UML Plugin for the NetBeans: Looking for more Coding Hands

The Eidos UML project a side project of the NetBeans UML plugin, is looking for more coding hands. If you're interested in helping, please contact developers at developer at uml dot netbeans dot org or visit the Eidos UML wiki.

Sunday, February 20, 2011

NetBeans IDE 7.0 Beta 2 Available for Download

The NetBeans 7.0 Beta 2 can be downloaded here.

You can find what is new and noteworthy in the NetBeans for PHP here.

Thursday, January 20, 2011

Specification Pattern in Dojo

A specification is a powerful design pattern able to tell if a candidate object matches some criteria. The specification has a method isSatisfiedBy(entity) that returns true if all criteria are met by entity.

In simple terms, a Specification is a small encapsulated unit of logic that gives an answer to a simple straight forward question: “does it match?”

Using a Specification we will split the logic of how we make a selection, away from objects we are selecting.

The Specification Pattern was originally proposed by Martin Fowler and Eric Evans.

Here is Dojo implementation of the CompositeSpecification.

/*
 * The Specification Pattern JavaScript Implementation
 *
 * A specification is a powerful design pattern able to tell if a candidate object
 * matches some criteria. The specification has a method isSatisfiedBy(entity)
 * that returns true if all criteria are met by entity
 *
 * @example http://www.martinfowler.com/apsupp/spec.pdf
 * @example http://en.wikipedia.org/wiki/Specification_pattern
 * @example http://devlicio.us/blogs/jeff_perrin/archive/2006/12/13/the-specification-pattern.aspx
 * @example http://devlicio.us/blogs/casey/archive/2009/03/02/ddd-the-specification-pattern.aspx
 *
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @author    Goran Miskovic, schkovich[at]gmail[dot]com
 * @copyright 2010 Goran Miskovic ©
 * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
 * @version   SVN: $Id: CompositeSpecification.js 85 2011-03-02 22:26:46Z schkovich $
 */
dojo.provide("schkovich.CompositeSpecification");

dojo.declare("schkovich.CompositeSpecification", null, {
    constructor: function() {
        var AndSpecification = dojo.declare(schkovich.CompositeSpecification, {
            constructor: function(self, other) {
                var One = self;
                var Other = other;
                this.IsSatisfiedBy = function(entity) {
                    return One.IsSatisfiedBy(entity) && Other.IsSatisfiedBy(entity);
                }
            }
        });

        var OrSpecification = dojo.declare(schkovich.CompositeSpecification,  {
            constructor: function(self, other) {
                var One = self;
                var Other = other;
                this.IsSatisfiedBy = function(entity) {
                    return One.IsSatisfiedBy(entity) || Other.IsSatisfiedBy(entity);
                }
            }
        });

        var NotSpecification =  dojo.declare(schkovich.CompositeSpecification,  {
            constructor: function(self) {
                var Wrapped = self;
                this.IsSatisfiedBy = function(entity) {
                    return !Wrapped.IsSatisfiedBy(entity);
                }
            }
        });

        this.And = function(other) {
            return new AndSpecification(this, other);
        }

        this.Or = function(other) {
            return new OrSpecification(this, other);
        }

        this.Not = function() {
            return new NotSpecification(this);
        }
    }
})

We will setup very simple example. Imagine you are librarian and the students asks for a list of composers that where productive during Romantic or Post-Romantic epoch and have compositions in C-minor or G-major.

In order to simplify the example we will assume that those whose first name starts with letter C did compose something in C-minor and that those whose first name starts on G composed something in G-major. :)

Data are structured in this way:
{"id":"1","firstName":"Johann Sebastian","lastName":"Bach","category":"Baroque"}
First we will need to create epoch specification:
var EpochSpecification = dojo.declare(
                schkovich.CompositeSpecification, {
                    constructor: function(epoch) {
                        this.IsSatisfiedBy = function(entity) {
                            return entity.category
                                && (entity.category === epoch);
                        }
                    }
                });

Then we need logic to select ones having compositions in C-minor or G-major or as we assumed ones whose first name starts with letter C or G:
var FirstLetterSpecification = dojo.declare(
                schkovich.CompositeSpecification, {
                    constructor: function(firstLetter) {
                        this.IsSatisfiedBy = function(entity) {
                            return entity.firstName
                                && (entity.firstName.indexOf(firstLetter) === 0);
                        }
                    }
                });

Once we defined conditions we will put them in a single specification:
var spec = new EpochSpecification("Romantic").
                    Or(new EpochSpecification("Post-Romantic")).
                    And(
                        new FirstLetterSpecification("G")
                        .Or(new FirstLetterSpecification("C"))
                    );

Finally we need a function that will filter data:
var filterComposers = function(specification) {
                    var filtered = dojo.filter(composersData.items, function(entity){
                        return specification.IsSatisfiedBy(entity);
                    });
                    composersData.items = filtered;
                    return composersData;
                }

Here is the HTML page that puts all of the above together:
<!DOCTYPE html>
<html>
    <head>
        <title>The Dojo Toolkit:: The Specification Pattern</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link rel="stylesheet"
              type="text/css"
              href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/claro/claro.css"
              />
        <style type="text/css">
            @import "http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojox/grid/resources/claroGrid.css";
            html, body {
                width: 100%;
                margin: 0;
                font-family:helvetica,arial,sans-serif; font-size:90%;
            }
            .dojoxGrid table {margin: 0;}
            #gridPlaceholder {height:314px; width: 50%}
        </style>
    </head>
    <body class="claro">
        <div id="gridPlaceholder"></div>
        <script type="text/javascript">
            djConfig={
                parseOnLoad: false,
                isDebug: false,
                baseUrl: "./",
                modulePaths: {schkovich: "./resources/schkovich" }
            };
        </script>
        <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js">
        </script>
        <script>
            dojo.require("dojo.data.ItemFileReadStore");
            dojo.require("dojox.grid.DataGrid");
            dojo.require("dijit.form.Button");
            dojo.require("schkovich.CompositeSpecification")

            dojo.addOnLoad(function() {
                dojo.parser.parse();
                var composersData = {
                    identifier: "id",
                    label: 'firstName',
                    items: [
                        {"id":"1","firstName":"Johann Sebastian","lastName":"Bach","category":"Baroque"},
                        {"id":"2","firstName":"Arcangelo","lastName":"Corelli","category":"Baroque"},
                        {"id":"3","firstName":"George Frideric","lastName":"Handel","category":"Baroque"},
                        {"id":"4","firstName":"Henry","lastName":"Purcell","category":"Baroque"},
                        {"id":"5","firstName":"Jean-Philippe","lastName":"Rameau","category":"Baroque"},
                        {"id":"6","firstName":"Domenico","lastName":"Scarlatti","category":"Baroque"},
                        {"id":"7","firstName":"Antonio","lastName":"Vivaldi","category":"Baroque"},
                        {"id":"8","firstName":"Ludwig van","lastName":"Beethoven","category":"Classical"},
                        {"id":"9","firstName":"Johannes","lastName":"Brahms","category":"Classical"},
                        {"id":"10","firstName":"Francesco","lastName":"Cavalli","category":"Classical"},
                        {"id":"11","firstName":"Fryderyk Franciszek","lastName":"Chopin","category":"Classical"},
                        {"id":"12","firstName":"Antonin","lastName":"Dvorak","category":"Classical"},
                        {"id":"13","firstName":"Franz Joseph","lastName":"Haydn","category":"Classical"},
                        {"id":"14","firstName":"Gustav","lastName":"Mahler","category":"Classical"},
                        {"id":"15","firstName":"Wolfgang Amadeus","lastName":"Mozart","category":"Classical"},
                        {"id":"16","firstName":"Johann","lastName":"Pachelbel","category":"Classical"},
                        {"id":"17","firstName":"Gioachino","lastName":"Rossini","category":"Classical"},
                        {"id":"18","firstName":"Dmitry","lastName":"Shostakovich","category":"Classical"},
                        {"id":"19","firstName":"Richard","lastName":"Wagner","category":"Classical"},
                        {"id":"20","firstName":"Louis-Hector","lastName":"Berlioz","category":"Romantic"},
                        {"id":"21","firstName":"Georges","lastName":"Bizet","category":"Romantic"},
                        {"id":"22","firstName":"Cesar","lastName":"Cui","category":"Romantic"},
                        {"id":"23","firstName":"Claude","lastName":"Debussy","category":"Romantic"},
                        {"id":"24","firstName":"Edward","lastName":"Elgar","category":"Romantic"},
                        {"id":"25","firstName":"Gabriel","lastName":"Faure","category":"Romantic"},
                        {"id":"26","firstName":"Cesar","lastName":"Franck","category":"Romantic"},
                        {"id":"27","firstName":"Edvard","lastName":"Grieg","category":"Romantic"},
                        {"id":"28","firstName":"Nikolay","lastName":"Rimsky-Korsakov","category":"Romantic"},
                        {"id":"29","firstName":"Franz Joseph","lastName":"Liszt","category":"Romantic"},
                        {"id":"30","firstName":"Felix","lastName":"Mendelssohn","category":"Romantic"},
                        {"id":"31","firstName":"Giacomo","lastName":"Puccini","category":"Romantic"},
                        {"id":"32","firstName":"Sergei","lastName":"Rachmaninoff","category":"Romantic"},
                        {"id":"33","firstName":"Camille","lastName":"Saint-Saens","category":"Romantic"},
                        {"id":"34","firstName":"Franz","lastName":"Schubert","category":"Romantic"},
                        {"id":"35","firstName":"Robert","lastName":"Schumann","category":"Romantic"},
                        {"id":"36","firstName":"Jean","lastName":"Sibelius","category":"Romantic"},
                        {"id":"37","firstName":"Bedrich","lastName":"Smetana","category":"Romantic"},
                        {"id":"38","firstName":"Richard","lastName":"Strauss","category":"Romantic"},
                        {"id":"39","firstName":"Pyotr Il'yich","lastName":"Tchaikovsky","category":"Romantic"},
                        {"id":"40","firstName":"Guiseppe","lastName":"Verdi","category":"Romantic"},
                        {"id":"41","firstName":"Bela","lastName":"Bartok","category":"Post-Romantic"},
                        {"id":"42","firstName":"Leonard","lastName":"Bernstein","category":"Post-Romantic"},
                        {"id":"43","firstName":"Benjamin","lastName":"Britten","category":"Post-Romantic"},
                        {"id":"44","firstName":"John","lastName":"Cage","category":"Post-Romantic"},
                        {"id":"45","firstName":"Aaron","lastName":"Copland","category":"Post-Romantic"},
                        {"id":"46","firstName":"George","lastName":"Gershwin","category":"Post-Romantic"},
                        {"id":"47","firstName":"Sergey","lastName":"Prokofiev","category":"Post-Romantic"},
                        {"id":"48","firstName":"Maurice","lastName":"Ravel","category":"Post-Romantic"},
                        {"id":"49","firstName":"Igor","lastName":"Stravinsky","category":"Post-Romantic"},
                        {"id":"50","firstName":"Carl","lastName":"Orff","category":"Post-Romantic"}
                    ]
                };
                var EpochSpecification = dojo.declare(
                schkovich.CompositeSpecification, {
                    constructor: function(epoch) {
                        this.IsSatisfiedBy = function(entity) {
                            return entity.category
                                && (entity.category === epoch);
                        }
                    }
                });
                var FirstLetterSpecification = dojo.declare(
                schkovich.CompositeSpecification, {
                    constructor: function(firstLetter) {
                        this.IsSatisfiedBy = function(entity) {
                            return entity.firstName
                                && (entity.firstName.indexOf(firstLetter) === 0);
                        }
                    }
                });
                var filterComposers = function(specification) {
                    var filtered = dojo.filter(composersData.items, function(entity){
                        return specification.IsSatisfiedBy(entity);
                    });
                    composersData.items = filtered;
                    return composersData;
                }
                var layout = [
                    {
                        name: "First Name",
                        field: "firstName",
                        width: "40%"
                    },

                    {
                        name: "Last Name",
                        field: "lastName",
                        width: "40%"
                    },
                    {
                        name: "Epoque",
                        field: "category",
                        width: "20%"
                    }
                ];
                var spec = new EpochSpecification("Romantic").
                    Or(new EpochSpecification("Post-Romantic")).
                    And(
                        new FirstLetterSpecification("G")
                        .Or(new FirstLetterSpecification("C"))
                    );
                var store = new dojo.data.ItemFileReadStore({
                    data: filterComposers(spec)
                });
                var grid = new dojox.grid.DataGrid({
                    store: store,
                    structure: layout,
                    clientSort: true,
                    rowSelector: '20px',
                    query: {firstName: "*"}
                }, dojo.doc.createElement("div"));
                dojo.place(grid.domNode, dojo.byId("gridPlaceholder"), "first");
                grid.startup();
            });
        </script>
    </body>
</html>

In reality I been using the Specification Pattern to handle highly complex forms having predefined sequence or entering data and numerous validation conditions. In short, each change of input data will trigger a loop over the form elements making sure that each element meets specification.

I am also considering creating data store based on the Specification Pattern: Instead passing complex queries a Specification will be passed.

Sunday, January 9, 2011

Goodbye to VMD

If you are running Linux 64 bit don't even consider using NetBenas Visual Mobile Designer (VMD). Actually do not consider developing mobile applications on any 64 bit platform (Linux, Mac or Windows).

The newer feature-rich Java ME SDK 3.0 is available only for Windows XP and Vista while the old Sun Java Wireless Toolkit 2.5.2 is available only for Linux 32 bit platforms.

In short, although you will see on the NetBeans download page that Java ME is supported technology for Linux 64 bit platform that is not true. One can create project but it is not possible to compile, run or deploy it.

You can try to install JDK 32 bit on Linux 64 bit but eventually you will run into problems.

у вези са: J2ME-er: HelloWorld using NetBeans' Visual Mobile Designer (VMD) (прикажи на Google Sidewiki-ју)

Monday, December 27, 2010

Converting PHP_CodeSniffer XML report into HTML page (XSLT)

PHP CodeSniffer XML report will look similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<phpcs version="1.2.2">
 <file name="/home/myhome/NetBeansProjects/LazyLoad/Libs/LazyLoad/Repositories/NestedSetNode.php" errors="7" warnings="1">
  <error line="39" column="11" source="Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase">Constants must be uppercase; expected LAZYLOAD but found LazyLoad</error>
  <error line="39" column="20" source="Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase">Constants must be uppercase; expected REPOSITORIES but found Repositories</error>
  <error line="58" column="1" source="PEAR.Commenting.ClassComment">Missing @link tag in class comment</error>
  <error line="69" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;set&quot; must be prefixed with an underscore</error>
  <error line="75" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;lft&quot; must be prefixed with an underscore</error>
  <error line="81" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;rgt&quot; must be prefixed with an underscore</error>
  <warning line="148" column="9" source="PEAR.ControlStructures.InlineControlStructure">Inline control structures are discouraged</warning>
 </file>
</phpcs>
To transform PHP CodeSniffer XML report into human readable format XSLT stylesheet needs to be attached:
<?xml version="1.0" encoding="UTF-8"?>
<!-- add line -->
<?xml-stylesheet type="text/xsl" href="phpcs.xsl"?>
<!-- rest of PHP CodeSniffer XML report -->
XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?>

<!--
    Document   : phpcs.xsl
    Created on : December 27, 2010, 1:42 PM
    Author     : schkovich
    Description:
        Transformation PHP_CodeSniffer xml report into human readable format.
-->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"  encoding="UTF-8"/>

    <!-- TODO customize transformation rules
         syntax recommendation http://www.w3.org/TR/xslt
    -->
    <xsl:template match="/">
        <html>
            <head>
                <title>phpcs.xsl</title>
                <link href="./phpcs.css" rel="stylesheet" type="text/css" />
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <th class="file">Name</th>
                            <th class="notes">Errors</th>
                            <th class="notes">Warnings</th>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:for-each select="phpcs/file">
                            <tr>
                                <td>
                                    <xsl:value-of select="@name" />
                                </td>
                                <td>
                                    <xsl:value-of select="@errors" />
                                </td>
                                <td>
                                    <xsl:value-of select="@warnings" />
                                </td>
                            </tr>
                            <tr>
                                <td colspan="3">
                                    <xsl:for-each select="error">
                                        <span class="error">Error: </span>
                                        <xsl:value-of select="self::node()"/>
                                        <br />
                                        <b>Line:</b>
                                        <xsl:value-of select="@line" />
                                        <br />
                                        <b>Column:</b>
                                        <xsl:value-of select="@column" />
                                        <br />
                                        <b>Source:</b>
                                        <xsl:value-of select="@source" />
                                        <hr />
                                    </xsl:for-each>
                                    <xsl:for-each select="warning">
                                        <span class="warning">Warning: </span>
                                        <xsl:value-of select="self::node()"/>
                                        <br />
                                        <b>Line:</b>
                                        <xsl:value-of select="@line" />
                                        <br />
                                        <b>Column:</b>
                                        <xsl:value-of select="@column" />
                                        <br />
                                        <b>Source:</b>
                                        <xsl:value-of select="@source" />
                                        <hr />
                                    </xsl:for-each>
                                </td>
                            </tr>
                        </xsl:for-each>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>
CSS
/*
    Document   : phpcs
    Created on : Dec 27, 2010, 1:34:02 PM
    Author     : schkovich
    Description:
        This stylesheet is designed to style phpcs XSLT stylessheet
*/

/*
   TODO customize this sample style
   Syntax recommendation http://www.w3.org/TR/REC-CSS2/
*/
table {
    width: 100%
}
th {
    text-align: left
}
th.file {
    width: 80%;
    color: green
}
th.notes {
    width: 10%;
    color: blue
}
span {
    font-weight: bold
}
span.error {
    color: red
}
span.warning {
    color: orange
}
Output will look like this:
To generate PHP CodeSniffer reports that will always have attached external XSLT stylesheet one PHP CodeSniffer file needs to be patched and one needs to be added.
New code generator CodeSniffer/Reports.Xls.php needs to be added:
<?php
/**
 * Xsl report for PHP_CodeSniffer.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Gabriele Santini <gsantini@sqli.com>
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Goran Miskovic <schkovich@gmail.com>
 * @copyright 2009 SQLI <www.sqli.com>
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   CVS: $Id: IsCamelCapsTest.php 240585 2007-08-02 00:05:40Z squiz $
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

/**
 * Xml report for PHP_CodeSniffer.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Gabriele Santini <gsantini@sqli.com>
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Goran Miskovic <schkovich@gmail.com>
 * @copyright 2009 SQLI <www.sqli.com>
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   Release: 1.2.2
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class PHP_CodeSniffer_Reports_Xsl implements PHP_CodeSniffer_Report
{


    /**
     * Prints all violations for processed files, in a proprietary XML format.
     *
     * Errors and warnings are displayed together, grouped by file.
     * 
     * External XSLT stylesheet phpcs.xsl will be attached
     *
     * @param array   $report       Prepared report.
     * @param boolean $showWarnings Show warnings?
     * @param boolean $showSources  Show sources?
     * @param int     $width        Maximum allowed lne width.
     * 
     * @return string 
     */
    public function generate(
        $report,
        $showWarnings=true,
        $showSources=false,
        $width=80
    ) {
        echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
        echo '<?xml-stylesheet type="text/xsl" href="phpcs.xsl"?>'.PHP_EOL;
        echo '<phpcs version="1.2.2">'.PHP_EOL;

        $errorsShown = 0;

        foreach ($report['files'] as $filename => $file) {
            if (empty($file['messages']) === true) {
                continue;
            }

            echo ' <file name="'.$filename.'" errors="'.$file['errors'].'" warnings="'.$file['warnings'].'">'.PHP_EOL;

            foreach ($file['messages'] as $line => $lineErrors) {
                foreach ($lineErrors as $column => $colErrors) {
                    foreach ($colErrors as $error) {
                        $error['type'] = strtolower($error['type']);
                        echo '  <'.$error['type'].' line="'.$line.'" column="'.$column.'" source="'.$error['source'].'">';
                        echo htmlspecialchars($error['message']).'</'.$error['type'].'>'.PHP_EOL;
                        $errorsShown++;
                    }
                }
            }//end foreach

            echo ' </file>'.PHP_EOL;
        }//end foreach

        echo '</phpcs>'.PHP_EOL;

        return $errorsShown;

    }//end generate()


}//end class

?>
CodeSniffer/CLI.php needs to be patched. Find variable $validReports on line 262 and add 'xsl' as the last array element:
$validReports     = array(
                                     'full',
                                     'xml',
                                     'checkstyle',
                                     'csv',
                                     'emacs',
                                     'source',
                                     'summary',
                                     'svnblame',
                                     'xsl',
                                    );
Finally run from the command line:
phpcs --report=xsl --report-file=${HOME}/NetBeansProjects/LazyLoad/public_html/LazyLoad.xml NetBeansProjects/LazyLoad/Libs

Sunday, December 12, 2010

Help Petr to Improve the NetBeans IDE Performance

Petr Pisl is asking community in his latest post on The NetBeans PHP Team Blog to help him identify use case when PHP editor is slow.

Tuesday, November 9, 2010

NetCAT 7.0 Started

NetBeans IDE 7.0 Community Acceptance Testing program officially started yesterday.

The goal of NetCAT 7.0 program is very simple: stabilize NetBeans IDE 7.0. Over seventy active NetBeans community members will be involved in testing the NetBeans IDE development builds during next four months.

This is a brief time schedule of important announcements, milestones and tasks:
  • 11/1 - Program announced on nbusers and nbdev lists. Volunteers joining NetCAT.
  • 11/8 - NetCAT team starts testing daily builds.
  • 11/12 - NetBeans 7.0 Beta #1 release
  • 11/16 - First weekly report sent.
  • 12/24 - 12/31 - Christmas break
  • 1/18 - NetBeans 7.0 Beta #2 release
  • 1/19 - Early CA research
  • 1/20 - Documentation review
  • 1/28 - Final CA survey questions defined.
  • 2/10 - NetBeans 7.0 RC release, CA survey opened
  • 2/28 - CA survey closed, results processed.
  • 3/1 - Last weekly report sent.
  • 3/8 - NetBeans 7.0 FCS release, NetCAT game over

Thursday, October 28, 2010

Porting C# Code to PHP (Porter Stemming Algorithm)

Jeff Mose wrote absolutely great article Notes from porting C# code to PHP. It is perhaps the most objective article about PHP ever.

I was inspired to review porting of Kamil Bartocha C# implementation of the English (Porter2) stemming algorithm that I did few years ago.

My native language is PHP and, of course, my view on the problem is from the opposite direction.

PHP and C# are similar. It was not hard to understand C# code and port it to PHP. However, I do know that it will be much, much harder for me to do it vice versa.

The most notable difference was set of string manipulation functions that comes out of box in PHP. Although there was no need to port C# StringBuilder class I did it for the sake of clarity.

I made few changes to make it run on PHP 5.3 and added the test page. Porter2 Stemming Algorithm in PHP 5.3 project is available at Kenai.

Unfortunately, I made bad choice to use flat ini formatted text file to store sample English vocabulary and its stemmed equivalent. As a consequence, I have had to remove around 20 words like false, true, on, off, etc.

Since the vocabulary has almost 30.000 words if you decide to run the test you will have to increase maximum execution time in php.ini.

I am working on both problems. Most likely I will try to store the vocabulary and its stemmed equivalent in XML format. Further on, I am looking into option to use Dojo Data Grid to display test results.