<commit_before>#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

void detectAndDraw( Mat& img,
                   CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
                   double scale);

String cascadeName =
"../../data/haarcascades/haarcascade_frontalface_alt.xml";
String nestedCascadeName =
"../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";

int main( int argc, const char** argv )
{
    CvCapture* capture = 0;
    Mat frame, frameCopy, image;
    const String scaleOpt = "--scale=";
    size_t scaleOptLen = scaleOpt.length();
    const String cascadeOpt = "--cascade=";
    size_t cascadeOptLen = cascadeOpt.length();
    const String nestedCascadeOpt = "--nested-cascade";
    size_t nestedCascadeOptLen = nestedCascadeOpt.length();
    String inputName;

    CascadeClassifier cascade, nestedCascade;
    double scale = 1;

    for( int i = 1; i < argc; i++ )
    {
        if( cascadeOpt.compare( 0, cascadeOptLen, argv[i], cascadeOptLen ) == 0 )
            cascadeName.assign( argv[i] + cascadeOptLen );
        else if( nestedCascadeOpt.compare( 0, nestedCascadeOptLen, argv[i], nestedCascadeOptLen ) == 0 )
        {
            if( argv[i][nestedCascadeOpt.length()] == '=' )
                nestedCascadeName.assign( argv[i] + nestedCascadeOpt.length() + 1 );
            if( !nestedCascade.load( nestedCascadeName ) )
                cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
        }
        else if( scaleOpt.compare( 0, scaleOptLen, argv[i], scaleOptLen ) == 0 )
        {
            if( !sscanf( argv[i] + scaleOpt.length(), "%lf", &scale ) || scale < 1 )
                scale = 1;
        }
        else if( argv[i][0] == '-' )
        {
            cerr << "WARNING: Unknown option %s" << argv[i] << endl;
        }
        else
            inputName.assign( argv[i] );
    }

    if( !cascade.load( cascadeName ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
        cerr << "Usage: facedetect [--cascade=\"<cascade_path>\"]\n"
            "   [--nested-cascade[=\"nested_cascade_path\"]]\n"
            "   [--scale[=<image scale>\n"
            "   [filename|camera_index]\n" ;
        return -1;
    }

    if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') )
        capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' );
    else if( inputName.size() )
    {
        image = imread( inputName, 1 );
        if( image.empty() )
            capture = cvCaptureFromAVI( inputName.c_str() );
    }
    else
        image = imread( "lena.jpg", 1 );

    cvNamedWindow( "result", 1 );

    if( capture )
    {
        for(;;)
        {
            IplImage* iplImg = cvQueryFrame( capture );
            frame = iplImg;
            if( frame.empty() )
                break;
            if( iplImg->origin == IPL_ORIGIN_TL )
                frame.copyTo( frameCopy );
            else
                flip( frame, frameCopy, 0 );

            detectAndDraw( frameCopy, cascade, nestedCascade, scale );

            if( waitKey( 10 ) >= 0 )
                goto _cleanup_;
        }

        waitKey(0);
_cleanup_:
        cvReleaseCapture( &capture );
    }
    else
    {
        if( !image.empty() )
        {
            detectAndDraw( image, cascade, nestedCascade, scale );
            waitKey(0);
        }
        else if( !inputName.empty() )
        {
            /* assume it is a text file containing the
            list of the image filenames to be processed - one per line */
            FILE* f = fopen( inputName.c_str(), "rt" );
            if( f )
            {
                char buf[1000+1];
                while( fgets( buf, 1000, f ) )
                {
                    int len = (int)strlen(buf), c;
                    while( len > 0 && isspace(buf[len-1]) )
                        len--;
                    buf[len] = '\0';
                    cout << "file " << buf << endl;
                    image = imread( buf, 1 );
                    if( !image.empty() )
                    {
                        detectAndDraw( image, cascade, nestedCascade, scale );
                        c = waitKey(0);
                        if( c == 27 || c == 'q' || c == 'Q' )
                            break;
                    }
                }
                fclose(f);
            }
        }
    }

    cvDestroyWindow("result");

    return 0;
}

void detectAndDraw( Mat& img,
                   CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
                   double scale)
{
    int i = 0;
    double t = 0;
    vector<Rect> faces;
    const static Scalar colors[] =  { CV_RGB(0,0,255),
        CV_RGB(0,128,255),
        CV_RGB(0,255,255),
        CV_RGB(0,255,0),
        CV_RGB(255,128,0),
        CV_RGB(255,255,0),
        CV_RGB(255,0,0),
        CV_RGB(255,0,255)} ;
    Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );

    cvtColor( img, gray, CV_BGR2GRAY );
    resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
    equalizeHist( smallImg, smallImg );

    t = (double)cvGetTickCount();
    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
        //|CV_HAAR_FIND_BIGGEST_OBJECT
        //|CV_HAAR_DO_ROUGH_SEARCH
        |CV_HAAR_SCALE_IMAGE
        ,
        Size(30, 30) );
    t = (double)cvGetTickCount() - t;
    printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
    for( vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++ )
    {
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;
        center.x = cvRound((r->x + r->width*0.5)*scale);
        center.y = cvRound((r->y + r->height*0.5)*scale);
        radius = cvRound((r->width + r->height)*0.25*scale);
        circle( img, center, radius, color, 3, 8, 0 );
        if( nestedCascade.empty() )
            continue;
        smallImgROI = smallImg(*r);
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
            //|CV_HAAR_FIND_BIGGEST_OBJECT
            //|CV_HAAR_DO_ROUGH_SEARCH
            //|CV_HAAR_DO_CANNY_PRUNING
            |CV_HAAR_SCALE_IMAGE
            ,
            Size(30, 30) );
        for( vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++ )
        {
            center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
            center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
            radius = cvRound((nr->width + nr->height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        }
    }
    cv::imshow( "result", img );
}
<commit_msg>put in a bunch of checks, outputs and docs<commit_after>#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

void help()
{
    cout << "\n This program demonstrates the haar cascade recognizer\n"
    		"this classifier can recognize many ~rigid objects, it's most known use is for faces.\n"
    	"Usage:\n"
    	"./facedetect [--cascade=<cascade_path>]\n"
        "   [--nested-cascade[=nested_cascade_path]]\n"
        "   [--scale=<image scale greater or equal to 1>\n"
        "   [filename|camera_index]\n" << endl;;
}

void detectAndDraw( Mat& img,
                   CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
                   double scale);

String cascadeName =
"../../data/haarcascades/haarcascade_frontalface_alt.xml";
String nestedCascadeName =
"../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";

int main( int argc, const char** argv )
{
    CvCapture* capture = 0;
    Mat frame, frameCopy, image;
    const String scaleOpt = "--scale=";
    size_t scaleOptLen = scaleOpt.length();
    const String cascadeOpt = "--cascade=";
    size_t cascadeOptLen = cascadeOpt.length();
    const String nestedCascadeOpt = "--nested-cascade";
    size_t nestedCascadeOptLen = nestedCascadeOpt.length();
    String inputName;

    help();
    CascadeClassifier cascade, nestedCascade;
    double scale = 1;

    for( int i = 1; i < argc; i++ )
    {
    	cout << "Processing " << i << " " <<  argv[i] << endl;
        if( cascadeOpt.compare( 0, cascadeOptLen, argv[i], cascadeOptLen ) == 0 )
        {
            cascadeName.assign( argv[i] + cascadeOptLen );
            cout << "  from which we have cascadeName= " << cascadeName << endl;
        }
        else if( nestedCascadeOpt.compare( 0, nestedCascadeOptLen, argv[i], nestedCascadeOptLen ) == 0 )
        {
            if( argv[i][nestedCascadeOpt.length()] == '=' )
                nestedCascadeName.assign( argv[i] + nestedCascadeOpt.length() + 1 );
            if( !nestedCascade.load( nestedCascadeName ) )
                cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
        }
        else if( scaleOpt.compare( 0, scaleOptLen, argv[i], scaleOptLen ) == 0 )
        {
            if( !sscanf( argv[i] + scaleOpt.length(), "%lf", &scale ) || scale < 1 )
                scale = 1;
            cout << " from which we read scale = " << scale << endl;
        }
        else if( argv[i][0] == '-' )
        {
            cerr << "WARNING: Unknown option %s" << argv[i] << endl;
        }
        else
            inputName.assign( argv[i] );
    }

    if( !cascade.load( cascadeName ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
        cerr << "Usage: facedetect [--cascade=<cascade_path>]\n"
            "   [--nested-cascade[=nested_cascade_path]]\n"
            "   [--scale[=<image scale>\n"
            "   [filename|camera_index]\n" << endl ;
        return -1;
    }

    if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') )
    {
        capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' );
        int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ;
        if(!capture) cout << "Capture from CAM " <<  c << " didn't work" << endl;
    }
    else if( inputName.size() )
    {
        image = imread( inputName, 1 );
        if( image.empty() )
        {
            capture = cvCaptureFromAVI( inputName.c_str() );
            if(!capture) cout << "Capture from AVI didn't work" << endl;
        }
   }
    else
    {
        image = imread( "lena.jpg", 1 );
        if(image.empty()) cout << "Couldn't read lena.jpg" << endl;
    }

    cvNamedWindow( "result", 1 );

    if( capture )
    {
    	cout << "In capture ..." << endl;
        for(;;)
        {
            IplImage* iplImg = cvQueryFrame( capture );
            frame = iplImg;
            if( frame.empty() )
                break;
            if( iplImg->origin == IPL_ORIGIN_TL )
                frame.copyTo( frameCopy );
            else
                flip( frame, frameCopy, 0 );

            detectAndDraw( frameCopy, cascade, nestedCascade, scale );

            if( waitKey( 10 ) >= 0 )
                goto _cleanup_;
        }

        waitKey(0);
_cleanup_:
        cvReleaseCapture( &capture );
    }
    else
    {
    	cout << "In image read" << endl;
        if( !image.empty() )
        {
            detectAndDraw( image, cascade, nestedCascade, scale );
            waitKey(0);
        }
        else if( !inputName.empty() )
        {
            /* assume it is a text file containing the
            list of the image filenames to be processed - one per line */
            FILE* f = fopen( inputName.c_str(), "rt" );
            if( f )
            {
                char buf[1000+1];
                while( fgets( buf, 1000, f ) )
                {
                    int len = (int)strlen(buf), c;
                    while( len > 0 && isspace(buf[len-1]) )
                        len--;
                    buf[len] = '\0';
                    cout << "file " << buf << endl;
                    image = imread( buf, 1 );
                    if( !image.empty() )
                    {
                        detectAndDraw( image, cascade, nestedCascade, scale );
                        c = waitKey(0);
                        if( c == 27 || c == 'q' || c == 'Q' )
                            break;
                    }
                    else
                    {
                    	cerr << "Aw snap, couldn't read image " << buf << endl;
                    }
                }
                fclose(f);
            }
        }
    }

    cvDestroyWindow("result");

    return 0;
}

void detectAndDraw( Mat& img,
                   CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
                   double scale)
{
    int i = 0;
    double t = 0;
    vector<Rect> faces;
    const static Scalar colors[] =  { CV_RGB(0,0,255),
        CV_RGB(0,128,255),
        CV_RGB(0,255,255),
        CV_RGB(0,255,0),
        CV_RGB(255,128,0),
        CV_RGB(255,255,0),
        CV_RGB(255,0,0),
        CV_RGB(255,0,255)} ;
    Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );

    cvtColor( img, gray, CV_BGR2GRAY );
    resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
    equalizeHist( smallImg, smallImg );

    t = (double)cvGetTickCount();
    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
        //|CV_HAAR_FIND_BIGGEST_OBJECT
        //|CV_HAAR_DO_ROUGH_SEARCH
        |CV_HAAR_SCALE_IMAGE
        ,
        Size(30, 30) );
    t = (double)cvGetTickCount() - t;
    printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
    for( vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++ )
    {
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;
        center.x = cvRound((r->x + r->width*0.5)*scale);
        center.y = cvRound((r->y + r->height*0.5)*scale);
        radius = cvRound((r->width + r->height)*0.25*scale);
        circle( img, center, radius, color, 3, 8, 0 );
        if( nestedCascade.empty() )
            continue;
        smallImgROI = smallImg(*r);
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
            //|CV_HAAR_FIND_BIGGEST_OBJECT
            //|CV_HAAR_DO_ROUGH_SEARCH
            //|CV_HAAR_DO_CANNY_PRUNING
            |CV_HAAR_SCALE_IMAGE
            ,
            Size(30, 30) );
        for( vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++ )
        {
            center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
            center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
            radius = cvRound((nr->width + nr->height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        }
    }
    cv::imshow( "result", img );
}
<|endoftext|>


<commit_before>/**
 * linklocator.cpp
 *
 * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com>
 *
 *  This file is part of KMail.
 *
 *  KMail is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "linklocator.h"
#include "pimemoticons.h"
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kstaticdeleter.h>
#include <kmdcodec.h>
#include <kdebug.h>

#include <qstylesheet.h>
#include <qfile.h>

QMap<QString, QString> *LinkLocator::s_smileyEmoticonNameMap = 0;
QMap<QString, QString> *LinkLocator::s_smileyEmoticonHTMLCache = 0;

static KStaticDeleter< QMap<QString, QString> > smileyMapDeleter;
static KStaticDeleter< QMap<QString, QString> > smileyCacheDeleter;

LinkLocator::LinkLocator(const QString& text, int pos)
  : mText(text), mPos(pos), mMaxUrlLen(4096), mMaxAddressLen(255)
{
  // If you change either of the above values for maxUrlLen or
  // maxAddressLen, then please also update the documentation for
  // setMaxUrlLen()/setMaxAddressLen() in the header file AND the
  // default values used for the maxUrlLen/maxAddressLen parameters
  // of convertToHtml().

  if ( !s_smileyEmoticonNameMap ) {
    smileyMapDeleter.setObject( s_smileyEmoticonNameMap,
                                new QMap<QString, QString>() );
    for ( int i = 0; i < EmotIcons::EnumSindex::COUNT; ++i ) {
      QString imageName( EmotIcons::EnumSindex::enumToString[i] );
      imageName.truncate( imageName.length() - 2 ); //remove the _0 bit
      s_smileyEmoticonNameMap->insert( EmotIcons::smiley(i), imageName );
    }
  }

  if ( !s_smileyEmoticonHTMLCache )
    smileyCacheDeleter.setObject( s_smileyEmoticonHTMLCache,
                                  new QMap<QString, QString>() );
}

void LinkLocator::setMaxUrlLen(int length)
{
  mMaxUrlLen = length;
}

int LinkLocator::maxUrlLen() const
{
  return mMaxUrlLen;
}

void LinkLocator::setMaxAddressLen(int length)
{
  mMaxAddressLen = length;
}

int LinkLocator::maxAddressLen() const
{
  return mMaxAddressLen;
}

QString LinkLocator::getUrl()
{
  QString url;
  if(atUrl())
  {
    // handle cases like this: <link>http://foobar.org/</link>
    int start = mPos;
    while(mPos < (int)mText.length() && mText[mPos] > ' ' && mText[mPos] != '"' &&
      QString("<>()[]").find(mText[mPos]) == -1)
    {
      ++mPos;
    }
    /* some URLs really end with:  # / &     */
    const QString allowedSpecialChars = QString("#/&");
    while(mPos > start && mText[mPos-1].isPunct() &&
		    allowedSpecialChars.find(mText[mPos-1]) == -1 )
    {
      --mPos;
    }

    url = mText.mid(start, mPos - start);
    if(isEmptyUrl(url) || mPos - start > maxUrlLen())
    {
      mPos = start;
      url = "";
    }
    else
    {
      --mPos;
    }
  }
  return url;
}

// keep this in sync with KMMainWin::slotUrlClicked()
bool LinkLocator::atUrl() const
{
  // the following characters are allowed in a dot-atom (RFC 2822):
  // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~");

  // the character directly before the URL must not be a letter, a number or
  // any other character allowed in a dot-atom (RFC 2822).
  if( ( mPos > 0 ) && ( mText[mPos-1].isLetterOrNumber() ||
                        ( allowedSpecialChars.find( mText[mPos-1] ) != -1 ) ) )
    return false;

  QChar ch = mText[mPos];
  return (ch=='h' && ( mText.mid(mPos, 7) == "http://" ||
                       mText.mid(mPos, 8) == "https://") ) ||
         (ch=='v' && mText.mid(mPos, 6) == "vnc://") ||
         (ch=='f' && ( mText.mid(mPos, 6) == "ftp://" ||
                       mText.mid(mPos, 7) == "ftps://") ) ||
         (ch=='s' && ( mText.mid(mPos, 7) == "sftp://" ||
                       mText.mid(mPos, 6) == "smb://") ) ||
         (ch=='m' && mText.mid(mPos, 7) == "mailto:") ||
         (ch=='w' && mText.mid(mPos, 4) == "www.") ||
         (ch=='f' && mText.mid(mPos, 4) == "ftp.");
         // note: no "file:" for security reasons
}

bool LinkLocator::isEmptyUrl(const QString& url)
{
  return url.isEmpty() ||
         url == "http://" ||
         url == "https://" ||
         url == "ftp://" ||
         url == "ftps://" ||
         url == "sftp://" ||
         url == "smb://" ||
         url == "vnc://" ||
         url == "mailto" ||
         url == "www" ||
         url == "ftp";
}

QString LinkLocator::getEmailAddress()
{
  QString address;

  if(mText[mPos] == '@')
  {
    // the following characters are allowed in a dot-atom (RFC 2822):
    // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
    const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~");

    // determine the local part of the email address
    int start = mPos - 1;
    while (start >= 0 && mText[start].unicode() < 128 &&
      (mText[start].isLetterOrNumber() ||
        mText[start] == '@' || // allow @ to find invalid email addresses
        allowedSpecialChars.find(mText[start]) != -1))
    {
      --start;
    }
    ++start;
    // we assume that an email address starts with a letter or a digit
    while (allowedSpecialChars.find(mText[start]) != -1)
      ++start;

    // determine the domain part of the email address
    int end = mPos + 1;
    while (end < (int)mText.length() &&
      (mText[end].isLetterOrNumber() ||
        mText[end] == '@' || // allow @ to find invalid email addresses
        allowedSpecialChars.find(mText[end]) != -1))
    {
      ++end;
    }
    // we assume that an email address ends with a letter or a digit
    while (allowedSpecialChars.find(mText[end - 1]) != -1)
      --end;

    address = mText.mid(start, end - start);
    if(isEmptyAddress(address) || end - start > maxAddressLen() || address.contains('@') != 1)
      address = "";

    if(!address.isEmpty())
      mPos = end - 1;
  }
  return address;
}

bool LinkLocator::isEmptyAddress(const QString& address)
{
  return address.isEmpty() ||
         address[0] == '@' ||
         address[address.length() - 1] == '@';
}

QString LinkLocator::convertToHtml(const QString& plainText, int flags,
  int maxUrlLen, int maxAddressLen)
{
  LinkLocator locator(plainText);
  locator.setMaxUrlLen(maxUrlLen);
  locator.setMaxAddressLen(maxAddressLen);

  QString str;
  QString result((QChar*)0, (int)locator.mText.length() * 2);
  QChar ch;
  int x;
  bool startOfLine = true;
  QString emoticon;

  for (locator.mPos = 0, x = 0; locator.mPos < (int)locator.mText.length(); locator.mPos++, x++)
  {
    ch = locator.mText[locator.mPos];
    if ( flags & PreserveSpaces )
    {
      if (ch==' ')
      {
        if (startOfLine) {
          result += " ";
          locator.mPos++, x++;
          startOfLine = false;
        }
        while (locator.mText[locator.mPos] == ' ')
        {
          result += " ";
          locator.mPos++, x++;
          if (locator.mText[locator.mPos] == ' ') {
            result += " ";
            locator.mPos++, x++;
          }
        }
        locator.mPos--, x--;
        continue;
      }
      else if (ch=='\t')
      {
        do
        {
          result += " ";
          x++;
        }
        while((x&7) != 0);
        x--;
        startOfLine = false;
        continue;
      }
    }
    if (ch=='\n')
    {
      result += "
";
      startOfLine = true;
      x = -1;
      continue;
    }

    startOfLine = false;
    if (ch=='&')
      result += "&";
    else if (ch=='"')
      result += """;
    else if (ch=='<')
      result += "<";
    else if (ch=='>')
      result += ">";
    else
    {
      const int start = locator.mPos;
      str = locator.getUrl();
      if(!str.isEmpty())
      {
        QString hyperlink;
        if(str.left(4) == "www.")
          hyperlink = "http://" + str;
        else if(str.left(4) == "ftp.")
          hyperlink = "ftp://" + str;
        else
          hyperlink = str;

	str = str.replace('&', "&");
        result += "" + str + "";
        x += locator.mPos - start;
      }
      else
      {
        str = locator.getEmailAddress();
        if(!str.isEmpty())
        {
          // len is the length of the local part
          int len = str.find('@');
          QString localPart = str.left(len);

          // remove the local part from the result (as '&'s have been expanded to
          // & we have to take care of the 4 additional characters per '&')
          result.truncate(result.length() - len - (localPart.contains('&')*4));
          x -= len;

          result += "" + str + "";
          x += str.length() - 1;
        }
        else {
          if ( flags & ReplaceSmileys )
            str = locator.getEmoticon();
          if ( ! str.isEmpty() ) {
            result += str;
            x += locator.mPos - start;
          }
          else {
            result += ch;
          }
        }
      }
    }
  }

  return result;
}

static QString pngToDataUrl( const QString & iconPath )
{
  if ( iconPath.isEmpty() )
    return QString::null;

  QFile pngFile( iconPath );
  if ( !pngFile.open( IO_ReadOnly | IO_Raw ) )
    return QString::null;

  QByteArray ba = pngFile.readAll();
  pngFile.close();
  return QString::fromLatin1("data:image/png;base64,%1")
         .arg( KCodecs::base64Encode( ba ) );
}

QString LinkLocator::getEmoticon()
{
  // smileys have to be prepended by whitespace
  if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() )
    return QString::null;

  // since smileys start with ':', ';', '(' or '8' short circuit method
  const QChar ch = mText[mPos];
  if ( ch !=':' && ch != ';' && ch != '(' && ch != '8' )
    return QString::null;

  // find the end of the smiley (a smiley is at most 4 chars long and ends at
  // lineend or whitespace)
  const int MinSmileyLen = 2;
  const int MaxSmileyLen = 4;
  int smileyLen = 1;
  while ( ( smileyLen <= MaxSmileyLen ) &&
          ( mPos+smileyLen < (int)mText.length() ) &&
          !mText[mPos+smileyLen].isSpace() )
    smileyLen++;
  if ( smileyLen < MinSmileyLen || smileyLen > MaxSmileyLen )
    return QString::null;

  const QString smiley = mText.mid( mPos, smileyLen );
  if ( !s_smileyEmoticonNameMap->contains( smiley ) )
    return QString::null; // that's not a (known) smiley

  QString htmlRep;
  if ( s_smileyEmoticonHTMLCache->contains( smiley ) ) {
    htmlRep = (*s_smileyEmoticonHTMLCache)[smiley];
  }
  else {
    const QString imageName = (*s_smileyEmoticonNameMap)[smiley];

#if KDE_IS_VERSION( 3, 3, 91 )
    const QString iconPath = locate( "emoticons",
                                     EmotIcons::theme() +
                                     QString::fromLatin1( "/" ) +
                                     imageName + QString::fromLatin1(".png") );
#else
    const QString iconPath = locate( "data",
                                     QString::fromLatin1( "kopete/pics/emoticons/" )+
                                     EmotIcons::theme() +
                                     QString::fromLatin1( "/" ) +
                                     imageName + QString::fromLatin1(".png") );
#endif

    const QString dataUrl = pngToDataUrl( iconPath );
    if ( dataUrl.isEmpty() ) {
      htmlRep = QString::null;
    }
    else {
      // create an image tag with nested span (the text in the span is used
      // for copy & paste) representing the smiley
      htmlRep = QString("%1"
                        "")
                .arg( QStyleSheet::escape( smiley ), dataUrl,
                      QStyleSheet::escape( smiley ),
                      QStyleSheet::escape( smiley ) );
    }
    s_smileyEmoticonHTMLCache->insert( smiley, htmlRep );
  }

  if ( !htmlRep.isEmpty() )
    mPos += smileyLen - 1;

  return htmlRep;
}

<commit_msg>* Backport fix #100662<commit_after>/**
 * linklocator.cpp
 *
 * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com>
 *
 *  This file is part of KMail.
 *
 *  KMail is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "linklocator.h"
#include "pimemoticons.h"
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kstaticdeleter.h>
#include <kmdcodec.h>
#include <kdebug.h>

#include <qstylesheet.h>
#include <qfile.h>

QMap<QString, QString> *LinkLocator::s_smileyEmoticonNameMap = 0;
QMap<QString, QString> *LinkLocator::s_smileyEmoticonHTMLCache = 0;

static KStaticDeleter< QMap<QString, QString> > smileyMapDeleter;
static KStaticDeleter< QMap<QString, QString> > smileyCacheDeleter;

LinkLocator::LinkLocator(const QString& text, int pos)
  : mText(text), mPos(pos), mMaxUrlLen(4096), mMaxAddressLen(255)
{
  // If you change either of the above values for maxUrlLen or
  // maxAddressLen, then please also update the documentation for
  // setMaxUrlLen()/setMaxAddressLen() in the header file AND the
  // default values used for the maxUrlLen/maxAddressLen parameters
  // of convertToHtml().

  if ( !s_smileyEmoticonNameMap ) {
    smileyMapDeleter.setObject( s_smileyEmoticonNameMap,
                                new QMap<QString, QString>() );
    for ( int i = 0; i < EmotIcons::EnumSindex::COUNT; ++i ) {
      QString imageName( EmotIcons::EnumSindex::enumToString[i] );
      imageName.truncate( imageName.length() - 2 ); //remove the _0 bit
      s_smileyEmoticonNameMap->insert( EmotIcons::smiley(i), imageName );
    }
  }

  if ( !s_smileyEmoticonHTMLCache )
    smileyCacheDeleter.setObject( s_smileyEmoticonHTMLCache,
                                  new QMap<QString, QString>() );
}

void LinkLocator::setMaxUrlLen(int length)
{
  mMaxUrlLen = length;
}

int LinkLocator::maxUrlLen() const
{
  return mMaxUrlLen;
}

void LinkLocator::setMaxAddressLen(int length)
{
  mMaxAddressLen = length;
}

int LinkLocator::maxAddressLen() const
{
  return mMaxAddressLen;
}

QString LinkLocator::getUrl()
{
  QString url;
  if(atUrl())
  {
    // handle cases like this: <link>http://foobar.org/</link>
    int start = mPos;
    while(mPos < (int)mText.length() && mText[mPos] > ' ' && mText[mPos] != '"' &&
      QString("<>()[]").find(mText[mPos]) == -1)
    {
      ++mPos;
    }
    /* some URLs really end with:  # / &     */
    const QString allowedSpecialChars = QString("#/&");
    while(mPos > start && mText[mPos-1].isPunct() &&
		    allowedSpecialChars.find(mText[mPos-1]) == -1 )
    {
      --mPos;
    }

    url = mText.mid(start, mPos - start);
    if(isEmptyUrl(url) || mPos - start > maxUrlLen())
    {
      mPos = start;
      url = "";
    }
    else
    {
      --mPos;
    }
  }
  return url;
}

// keep this in sync with KMMainWin::slotUrlClicked()
bool LinkLocator::atUrl() const
{
  // the following characters are allowed in a dot-atom (RFC 2822):
  // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~");

  // the character directly before the URL must not be a letter, a number or
  // any other character allowed in a dot-atom (RFC 2822).
  if( ( mPos > 0 ) && ( mText[mPos-1].isLetterOrNumber() ||
                        ( allowedSpecialChars.find( mText[mPos-1] ) != -1 ) ) )
    return false;

  QChar ch = mText[mPos];
  return (ch=='h' && ( mText.mid(mPos, 7) == "http://" ||
                       mText.mid(mPos, 8) == "https://") ) ||
         (ch=='v' && mText.mid(mPos, 6) == "vnc://") ||
         (ch=='f' && ( mText.mid(mPos, 6) == "ftp://" ||
                       mText.mid(mPos, 7) == "ftps://") ) ||
         (ch=='s' && ( mText.mid(mPos, 7) == "sftp://" ||
                       mText.mid(mPos, 6) == "smb://") ) ||
         (ch=='m' && mText.mid(mPos, 7) == "mailto:") ||
         (ch=='w' && mText.mid(mPos, 4) == "www.") ||
         (ch=='f' && mText.mid(mPos, 4) == "ftp.");
         // note: no "file:" for security reasons
}

bool LinkLocator::isEmptyUrl(const QString& url)
{
  return url.isEmpty() ||
         url == "http://" ||
         url == "https://" ||
         url == "ftp://" ||
         url == "ftps://" ||
         url == "sftp://" ||
         url == "smb://" ||
         url == "vnc://" ||
         url == "mailto" ||
         url == "www" ||
         url == "ftp";
}

QString LinkLocator::getEmailAddress()
{
  QString address;

  if(mText[mPos] == '@')
  {
    // the following characters are allowed in a dot-atom (RFC 2822):
    // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
    const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~");

    // determine the local part of the email address
    int start = mPos - 1;
    while (start >= 0 && mText[start].unicode() < 128 &&
      (mText[start].isLetterOrNumber() ||
        mText[start] == '@' || // allow @ to find invalid email addresses
        allowedSpecialChars.find(mText[start]) != -1))
    {
      --start;
    }
    ++start;
    // we assume that an email address starts with a letter or a digit
    while (allowedSpecialChars.find(mText[start]) != -1)
      ++start;

    // determine the domain part of the email address
    int end = mPos + 1;
    while (end < (int)mText.length() &&
      (mText[end].isLetterOrNumber() ||
        mText[end] == '@' || // allow @ to find invalid email addresses
        allowedSpecialChars.find(mText[end]) != -1))
    {
      ++end;
    }
    // we assume that an email address ends with a letter or a digit
    while (allowedSpecialChars.find(mText[end - 1]) != -1)
      --end;

    address = mText.mid(start, end - start);
    if(isEmptyAddress(address) || end - start > maxAddressLen() || address.contains('@') != 1)
      address = "";

    if(!address.isEmpty())
      mPos = end - 1;
  }
  return address;
}

bool LinkLocator::isEmptyAddress(const QString& address)
{
  return address.isEmpty() ||
         address[0] == '@' ||
         address[address.length() - 1] == '@';
}

QString LinkLocator::convertToHtml(const QString& plainText, int flags,
  int maxUrlLen, int maxAddressLen)
{
  LinkLocator locator(plainText);
  locator.setMaxUrlLen(maxUrlLen);
  locator.setMaxAddressLen(maxAddressLen);

  QString str;
  QString result((QChar*)0, (int)locator.mText.length() * 2);
  QChar ch;
  int x;
  bool startOfLine = true;
  QString emoticon;

  for (locator.mPos = 0, x = 0; locator.mPos < (int)locator.mText.length(); locator.mPos++, x++)
  {
    ch = locator.mText[locator.mPos];
    if ( flags & PreserveSpaces )
    {
      if (ch==' ')
      {
        if (startOfLine) {
          result += " ";
          locator.mPos++, x++;
          startOfLine = false;
        }
        while (locator.mText[locator.mPos] == ' ')
        {
          result += " ";
          locator.mPos++, x++;
          if (locator.mText[locator.mPos] == ' ') {
            result += " ";
            locator.mPos++, x++;
          }
        }
        locator.mPos--, x--;
        continue;
      }
      else if (ch=='\t')
      {
        do
        {
          result += " ";
          x++;
        }
        while((x&7) != 0);
        x--;
        startOfLine = false;
        continue;
      }
    }
    if (ch=='\n')
    {
      result += "
";
      startOfLine = true;
      x = -1;
      continue;
    }

    startOfLine = false;
    if (ch=='&')
      result += "&";
    else if (ch=='"')
      result += """;
    else if (ch=='<')
      result += "<";
    else if (ch=='>')
      result += ">";
    else
    {
      const int start = locator.mPos;
      str = locator.getUrl();
      if(!str.isEmpty())
      {
        QString hyperlink;
        if(str.left(4) == "www.")
          hyperlink = "http://" + str;
        else if(str.left(4) == "ftp.")
          hyperlink = "ftp://" + str;
        else
          hyperlink = str;

	str = str.replace('&', "&");
        result += "" + str + "";
        x += locator.mPos - start;
      }
      else
      {
        str = locator.getEmailAddress();
        if(!str.isEmpty())
        {
          // len is the length of the local part
          int len = str.find('@');
          QString localPart = str.left(len);

          // remove the local part from the result (as '&'s have been expanded to
          // & we have to take care of the 4 additional characters per '&')
          result.truncate(result.length() - len - (localPart.contains('&')*4));
          x -= len;

          result += "" + str + "";
          x += str.length() - 1;
        }
        else {
          if ( flags & ReplaceSmileys )
            str = locator.getEmoticon();
          if ( ! str.isEmpty() ) {
            result += str;
            x += locator.mPos - start;
          }
          else {
            result += ch;
          }
        }
      }
    }
  }

  return result;
}

static QString pngToDataUrl( const QString & iconPath )
{
  if ( iconPath.isEmpty() )
    return QString::null;

  QFile pngFile( iconPath );
  if ( !pngFile.open( IO_ReadOnly | IO_Raw ) )
    return QString::null;

  QByteArray ba = pngFile.readAll();
  pngFile.close();
  return QString::fromLatin1("data:image/png;base64,%1")
         .arg( KCodecs::base64Encode( ba ) );
}

QString LinkLocator::getEmoticon()
{
  // smileys have to be prepended by whitespace
  if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() )
    return QString::null;

  // since smileys start with ':', ';', '(' or '8' short circuit method
  const QChar ch = mText[mPos];
  if ( ch !=':' && ch != ';' && ch != '(' && ch != '8' )
    return QString::null;

  // find the end of the smiley (a smiley is at most 4 chars long and ends at
  // lineend or whitespace)
  const int MinSmileyLen = 2;
  const int MaxSmileyLen = 4;
  int smileyLen = 1;
  while ( ( smileyLen <= MaxSmileyLen ) &&
          ( mPos+smileyLen < (int)mText.length() ) &&
          !mText[mPos+smileyLen].isSpace() )
    smileyLen++;
  if ( smileyLen < MinSmileyLen || smileyLen > MaxSmileyLen )
    return QString::null;

  const QString smiley = mText.mid( mPos, smileyLen );
  if ( !s_smileyEmoticonNameMap->contains( smiley ) )
    return QString::null; // that's not a (known) smiley

  QString htmlRep;
  if ( s_smileyEmoticonHTMLCache->contains( smiley ) ) {
    htmlRep = (*s_smileyEmoticonHTMLCache)[smiley];
  }
  else {
    const QString imageName = (*s_smileyEmoticonNameMap)[smiley];

#if KDE_IS_VERSION( 3, 3, 91 )
    const QString iconPath = locate( "emoticons",
                                     EmotIcons::theme() +
                                     QString::fromLatin1( "/" ) +
                                     imageName + QString::fromLatin1(".png") );
#else
    const QString iconPath = locate( "data",
                                     QString::fromLatin1( "kopete/pics/emoticons/" )+
                                     EmotIcons::theme() +
                                     QString::fromLatin1( "/" ) +
                                     imageName + QString::fromLatin1(".png") );
#endif

    const QString dataUrl = pngToDataUrl( iconPath );
    if ( dataUrl.isEmpty() ) {
      htmlRep = QString::null;
    }
    else {
      // create an image tag (the text in attribute alt is used
      // for copy & paste) representing the smiley
      htmlRep = QString("")
                .arg( dataUrl,
                      QStyleSheet::escape( smiley ),
                      QStyleSheet::escape( smiley ) );
    }
    s_smileyEmoticonHTMLCache->insert( smiley, htmlRep );
  }

  if ( !htmlRep.isEmpty() )
    mPos += smileyLen - 1;

  return htmlRep;
}

<|endoftext|>


<commit_before>#include "JsArgToArrayConverter.h"
#include <limits>
#include <sstream>
#include <assert.h>
#include "ObjectManager.h"
#include "NativeScriptAssert.h"
#include "ArgConverter.h"
#include "JniLocalRef.h"
#include "Util.h"
#include "V8GlobalHelpers.h"
#include "V8StringConstants.h"
#include "JavaObjectArrayCache.h"
#include "JType.h"

using namespace v8;
using namespace std;
using namespace tns;

JsArgToArrayConverter::JsArgToArrayConverter(const v8::Handle<Value>& arg, bool isImplementationObject, int classReturnType) :
		m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(classReturnType)
{
	if (!isImplementationObject)
	{
		m_argsLen = 1;
		m_argsAsObject = new jobject[m_argsLen];
		memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject));

		m_isValid = ConvertArg(arg, 0);
	}
}

JsArgToArrayConverter::JsArgToArrayConverter(const v8::FunctionCallbackInfo<Value>& args, bool hasImplementationObject, const Handle<Object>& outerThis) :
		m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(static_cast<int>(Type::Null))
{
	auto isInnerClass = !outerThis.IsEmpty();
	if (isInnerClass)
	{
		m_argsLen = args.Length() + 1;
	}
	else
	{
		m_argsLen = !hasImplementationObject ? args.Length() : args.Length() - 2;
	}

	bool success = true;

	if (m_argsLen > 0)
	{
		m_argsAsObject = new jobject[m_argsLen];
		memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject));

		for (int i = 0; i < m_argsLen; i++)
		{
			if (isInnerClass)
			{
				if (i == 0)
				{
					success = ConvertArg(outerThis, i);
				}
				else
				{
					success = ConvertArg(args[i - 1], i);
				}
			}
			else
			{
				success = ConvertArg(args[i], i);
			}

			if (!success)
			{
				break;
			}
		}
	}

	m_isValid = success;
}

bool JsArgToArrayConverter::ConvertArg(const Handle<Value>& arg, int index)
{
	bool success = false;
	stringstream s;

	JEnv env;

	Type returnType = JType::getClassType(m_return_type);

	if (arg.IsEmpty())
	{
		s << "Cannot convert empty JavaScript object";
		success = false;
	}
	else if (arg->IsInt32() && (returnType == Type::Int || returnType == Type::Null))
	{
		jint value = arg->Int32Value();
		JniLocalRef javaObject(JType::NewInt(env, value));
		SetConvertedObject(env, index, javaObject);

		success = true;
	}
	else if (arg->IsNumber() || arg->IsNumberObject())
	{
		double d = arg->NumberValue();
		int64_t i = (int64_t)d;
		bool isInteger = d == i;

		if (isInteger)
		{
			jobject obj;
			if ((INT_MIN <= i) && (i <= INT_MAX) && (returnType == Type::Int || returnType == Type::Null))
			{
				obj = JType::NewInt(env, (jint)d);
			}
			else
			{
				obj = JType::NewLong(env, (jlong)d);
			}

			JniLocalRef javaObject(obj);
			SetConvertedObject(env, index, javaObject);

			success = true;
		}
		else
		{
			float f = (float)d;
			bool isFloat = (f == d);

			jobject obj;
			if(isFloat && (FLT_MIN <= f) && (f <= FLT_MAX) && (returnType == Type::Float)) {
				obj = JType::NewFloat(env, (jfloat)d);
			}
			else {/*isDouble*/
				obj = JType::NewDouble(env, (jdouble)d);
			}

			JniLocalRef javaObject(obj);
			SetConvertedObject(env, index, javaObject);

			success = true;
		}
	}
	else if (arg->IsBoolean())
	{
		jboolean value = arg->BooleanValue();
		JniLocalRef javaObject(JType::NewBoolean(env, value));
		SetConvertedObject(env, index, javaObject);
		success = true;
	}
	else if (arg->IsBooleanObject())
	{
		auto boolObj = Handle<BooleanObject>::Cast(arg);
		jboolean value = boolObj->BooleanValue() ? JNI_TRUE : JNI_FALSE;
		JniLocalRef javaObject(JType::NewBoolean(env, value));
		SetConvertedObject(env, index, javaObject);

		success = true;
	}
	else if (arg->IsString() || arg->IsStringObject())
	{
		JniLocalRef stringObject(ConvertToJavaString(arg));
		SetConvertedObject(env, index, stringObject);

		success = true;
	}
	else if (arg->IsObject())
	{
		Local<Object> objectWithHiddenID = arg->ToObject();

		auto hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsLong());
		if (!hidden.IsEmpty())
		{
			jlong value = 0;
			if (hidden->IsString())
			{
				auto strValue = ConvertToString(hidden->ToString());
				value = atoll(strValue.c_str());
			}
			else if (hidden->IsInt32())
			{
				value = hidden->ToInt32()->Int32Value();
			}

			JniLocalRef javaObject(JType::NewLong(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsByte());
		if (!hidden.IsEmpty())
		{
			jbyte value = 0;
			if (hidden->IsString())
			{
				string value = ConvertToString(hidden->ToString());
				int byteArg = atoi(value.c_str());
				value = (jbyte)byteArg;
			}
			else if (hidden->IsInt32())
			{
				int byteArg = hidden->ToInt32()->Int32Value();
				value = (jbyte)byteArg;
			}

			JniLocalRef javaObject(JType::NewByte(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsShort());
		if (!hidden.IsEmpty())
		{
			jshort value = 0;
			if (hidden->IsString())
			{
				string value = ConvertToString(hidden->ToString());
				int shortArg = atoi(value.c_str());
				value = (jshort)shortArg;
			}
			else if (hidden->IsInt32())
			{
				jlong shortArg = hidden->ToInt32()->Int32Value();
				value = (jshort)shortArg;
			}

			JniLocalRef javaObject(JType::NewShort(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsDouble());
		if (!hidden.IsEmpty())
		{
			jdouble value = 0;
			if (hidden->IsNumber())
			{
				double doubleArg = hidden->ToNumber()->NumberValue();
				value = (jdouble)doubleArg;
			}

			JniLocalRef javaObject(JType::NewDouble(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsFloat());
		if (!hidden.IsEmpty())
		{
			jfloat value = 0;
			if (hidden->IsNumber())
			{
				double floatArg = hidden->ToNumber()->NumberValue();
				value = (jfloat)floatArg;
			}

			JniLocalRef javaObject(JType::NewFloat(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsChar());
		if (!hidden.IsEmpty())
		{
			jchar value = '\0';
			if (hidden->IsString())
			{
				string str = ConvertToString(hidden->ToString());
				value = (jchar)str[0];
			}

			JniLocalRef javaObject(JType::NewChar(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		jweak obj = ObjectManager::GetJavaObjectByJsObjectStatic(objectWithHiddenID);
		success = obj != nullptr;
		if (success)
		{
			SetConvertedObject(env, index, obj, true);
		}
		else
		{
			s << "Cannot convert JavaScript object with id " << objectWithHiddenID->GetIdentityHash() << " at index " << index;
		}
	}
	else if (arg->IsUndefined() || arg->IsNull())
	{
		SetConvertedObject(env, index, nullptr);
		success = true;
	}
	else
	{
		s << "Cannot convert JavaScript object at index " << index;
		success = false;
	}

	if (!success)
	{
		m_error.index = index;
		m_error.msg = s.str();
	}

	return success;
}

jobject JsArgToArrayConverter::GetConvertedArg()
{
	return (m_argsLen > 0) ? m_argsAsObject[0] : nullptr;
}

void JsArgToArrayConverter::SetConvertedObject(JEnv& env, int index, jobject obj, bool isGlobalRef)
{
	if (obj == nullptr)
	{
		m_argsAsObject[index] = obj;
	}
	else if (isGlobalRef)
	{
		m_argsAsObject[index] = obj;
	}
	else
	{
		m_storedIndexes.push_back(index);
		m_argsAsObject[index] = env.NewLocalRef(obj);
	}
}

int JsArgToArrayConverter::Length() const
{
	return m_argsLen;
}

bool JsArgToArrayConverter::IsValid() const
{
	return m_isValid;
}

JsArgToArrayConverter::Error JsArgToArrayConverter::GetError() const
{
	return m_error;
}

jobjectArray JsArgToArrayConverter::ToJavaArray()
{
	if ((m_arr == nullptr) && (m_argsLen > 0))
	{
		m_arr = JavaObjectArrayCache::GetJavaObjectArray(m_argsLen);
	}

	if (m_argsLen > 0)
	{
		JEnv env;

		for (int i = 0; i < m_argsLen; i++)
		{
			env.SetObjectArrayElement(m_arr, i, m_argsAsObject[i]);
		}
	}

	return m_arr;
}

JsArgToArrayConverter::~JsArgToArrayConverter()
{
	if (m_argsLen > 0)
	{
		JEnv env;

		int length = m_storedIndexes.size();
		for (int i = 0; i < length; i++)
		{
			int index = m_storedIndexes[i];
			env.DeleteLocalRef(m_argsAsObject[index]);
		}

		delete[] m_argsAsObject;
	}
}
<commit_msg>fixed cast problems with and without return type<commit_after>#include "JsArgToArrayConverter.h"
#include <limits>
#include <sstream>
#include <assert.h>
#include "ObjectManager.h"
#include "NativeScriptAssert.h"
#include "ArgConverter.h"
#include "JniLocalRef.h"
#include "Util.h"
#include "V8GlobalHelpers.h"
#include "V8StringConstants.h"
#include "JavaObjectArrayCache.h"
#include "JType.h"

using namespace v8;
using namespace std;
using namespace tns;

JsArgToArrayConverter::JsArgToArrayConverter(const v8::Handle<Value>& arg, bool isImplementationObject, int classReturnType) :
		m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(classReturnType)
{
	if (!isImplementationObject)
	{
		m_argsLen = 1;
		m_argsAsObject = new jobject[m_argsLen];
		memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject));

		m_isValid = ConvertArg(arg, 0);
	}
}

JsArgToArrayConverter::JsArgToArrayConverter(const v8::FunctionCallbackInfo<Value>& args, bool hasImplementationObject, const Handle<Object>& outerThis) :
		m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(static_cast<int>(Type::Null))
{
	auto isInnerClass = !outerThis.IsEmpty();
	if (isInnerClass)
	{
		m_argsLen = args.Length() + 1;
	}
	else
	{
		m_argsLen = !hasImplementationObject ? args.Length() : args.Length() - 2;
	}

	bool success = true;

	if (m_argsLen > 0)
	{
		m_argsAsObject = new jobject[m_argsLen];
		memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject));

		for (int i = 0; i < m_argsLen; i++)
		{
			if (isInnerClass)
			{
				if (i == 0)
				{
					success = ConvertArg(outerThis, i);
				}
				else
				{
					success = ConvertArg(args[i - 1], i);
				}
			}
			else
			{
				success = ConvertArg(args[i], i);
			}

			if (!success)
			{
				break;
			}
		}
	}

	m_isValid = success;
}

bool JsArgToArrayConverter::ConvertArg(const Handle<Value>& arg, int index)
{
	bool success = false;
	stringstream s;

	JEnv env;

	Type returnType = JType::getClassType(m_return_type);

	if (arg.IsEmpty())
	{
		s << "Cannot convert empty JavaScript object";
		success = false;
	}
	else if (arg->IsInt32() && (returnType == Type::Int || returnType == Type::Null))
	{
		jint value = arg->Int32Value();
		JniLocalRef javaObject(JType::NewInt(env, value));
		SetConvertedObject(env, index, javaObject);

		success = true;
	}
	else if (arg->IsNumber() || arg->IsNumberObject())
	{
		double d = arg->NumberValue();
		int64_t i = (int64_t)d;
		bool isInteger = d == i;

		if (isInteger)
		{
			jobject obj;

			//if returnType is long it will cast to long
			//if there is no return type specified it will cast to int
			//because default return type is null (ref type)
			if ((INT_MIN <= i) && (i <= INT_MAX) && (returnType == Type::Int || returnType == Type::Null))
			{
				obj = JType::NewInt(env, (jint)d);
			}
			else /*isLong*/
			{
				obj = JType::NewLong(env, (jlong)d);
			}

			JniLocalRef javaObject(obj);
			SetConvertedObject(env, index, javaObject);

			success = true;
		}
		else
		{
			float f = (float)d;
			bool isFloat = (f == d);

			jobject obj;
			//if returnType is double it will cast to double
			//if there is no return type specified it will cast to float
			//because default return type is null (ref type)
			if(isFloat && (FLT_MIN <= f) && (f <= FLT_MAX) && (returnType == Type::Float || returnType == Type::Null)) {
				obj = JType::NewFloat(env, (jfloat)d);
			}
			else {/*isDouble*/
				obj = JType::NewDouble(env, (jdouble)d);
			}

			JniLocalRef javaObject(obj);
			SetConvertedObject(env, index, javaObject);

			success = true;
		}
	}
	else if (arg->IsBoolean())
	{
		jboolean value = arg->BooleanValue();
		JniLocalRef javaObject(JType::NewBoolean(env, value));
		SetConvertedObject(env, index, javaObject);
		success = true;
	}
	else if (arg->IsBooleanObject())
	{
		auto boolObj = Handle<BooleanObject>::Cast(arg);
		jboolean value = boolObj->BooleanValue() ? JNI_TRUE : JNI_FALSE;
		JniLocalRef javaObject(JType::NewBoolean(env, value));
		SetConvertedObject(env, index, javaObject);

		success = true;
	}
	else if (arg->IsString() || arg->IsStringObject())
	{
		JniLocalRef stringObject(ConvertToJavaString(arg));
		SetConvertedObject(env, index, stringObject);

		success = true;
	}
	else if (arg->IsObject())
	{
		Local<Object> objectWithHiddenID = arg->ToObject();

		auto hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsLong());
		if (!hidden.IsEmpty())
		{
			jlong value = 0;
			if (hidden->IsString())
			{
				auto strValue = ConvertToString(hidden->ToString());
				value = atoll(strValue.c_str());
			}
			else if (hidden->IsInt32())
			{
				value = hidden->ToInt32()->Int32Value();
			}

			JniLocalRef javaObject(JType::NewLong(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsByte());
		if (!hidden.IsEmpty())
		{
			jbyte value = 0;
			if (hidden->IsString())
			{
				string value = ConvertToString(hidden->ToString());
				int byteArg = atoi(value.c_str());
				value = (jbyte)byteArg;
			}
			else if (hidden->IsInt32())
			{
				int byteArg = hidden->ToInt32()->Int32Value();
				value = (jbyte)byteArg;
			}

			JniLocalRef javaObject(JType::NewByte(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsShort());
		if (!hidden.IsEmpty())
		{
			jshort value = 0;
			if (hidden->IsString())
			{
				string value = ConvertToString(hidden->ToString());
				int shortArg = atoi(value.c_str());
				value = (jshort)shortArg;
			}
			else if (hidden->IsInt32())
			{
				jlong shortArg = hidden->ToInt32()->Int32Value();
				value = (jshort)shortArg;
			}

			JniLocalRef javaObject(JType::NewShort(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsDouble());
		if (!hidden.IsEmpty())
		{
			jdouble value = 0;
			if (hidden->IsNumber())
			{
				double doubleArg = hidden->ToNumber()->NumberValue();
				value = (jdouble)doubleArg;
			}

			JniLocalRef javaObject(JType::NewDouble(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsFloat());
		if (!hidden.IsEmpty())
		{
			jfloat value = 0;
			if (hidden->IsNumber())
			{
				double floatArg = hidden->ToNumber()->NumberValue();
				value = (jfloat)floatArg;
			}

			JniLocalRef javaObject(JType::NewFloat(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		hidden = objectWithHiddenID->GetHiddenValue(V8StringConstants::GetMarkedAsChar());
		if (!hidden.IsEmpty())
		{
			jchar value = '\0';
			if (hidden->IsString())
			{
				string str = ConvertToString(hidden->ToString());
				value = (jchar)str[0];
			}

			JniLocalRef javaObject(JType::NewChar(env, value));
			SetConvertedObject(env, index, javaObject);
			return true;
		}

		jweak obj = ObjectManager::GetJavaObjectByJsObjectStatic(objectWithHiddenID);
		success = obj != nullptr;
		if (success)
		{
			SetConvertedObject(env, index, obj, true);
		}
		else
		{
			s << "Cannot convert JavaScript object with id " << objectWithHiddenID->GetIdentityHash() << " at index " << index;
		}
	}
	else if (arg->IsUndefined() || arg->IsNull())
	{
		SetConvertedObject(env, index, nullptr);
		success = true;
	}
	else
	{
		s << "Cannot convert JavaScript object at index " << index;
		success = false;
	}

	if (!success)
	{
		m_error.index = index;
		m_error.msg = s.str();
	}

	return success;
}

jobject JsArgToArrayConverter::GetConvertedArg()
{
	return (m_argsLen > 0) ? m_argsAsObject[0] : nullptr;
}

void JsArgToArrayConverter::SetConvertedObject(JEnv& env, int index, jobject obj, bool isGlobalRef)
{
	if (obj == nullptr)
	{
		m_argsAsObject[index] = obj;
	}
	else if (isGlobalRef)
	{
		m_argsAsObject[index] = obj;
	}
	else
	{
		m_storedIndexes.push_back(index);
		m_argsAsObject[index] = env.NewLocalRef(obj);
	}
}

int JsArgToArrayConverter::Length() const
{
	return m_argsLen;
}

bool JsArgToArrayConverter::IsValid() const
{
	return m_isValid;
}

JsArgToArrayConverter::Error JsArgToArrayConverter::GetError() const
{
	return m_error;
}

jobjectArray JsArgToArrayConverter::ToJavaArray()
{
	if ((m_arr == nullptr) && (m_argsLen > 0))
	{
		m_arr = JavaObjectArrayCache::GetJavaObjectArray(m_argsLen);
	}

	if (m_argsLen > 0)
	{
		JEnv env;

		for (int i = 0; i < m_argsLen; i++)
		{
			env.SetObjectArrayElement(m_arr, i, m_argsAsObject[i]);
		}
	}

	return m_arr;
}

JsArgToArrayConverter::~JsArgToArrayConverter()
{
	if (m_argsLen > 0)
	{
		JEnv env;

		int length = m_storedIndexes.size();
		for (int i = 0; i < length; i++)
		{
			int index = m_storedIndexes[i];
			env.DeleteLocalRef(m_argsAsObject[index]);
		}

		delete[] m_argsAsObject;
	}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2019 the Urho3D project.
//
// 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.
//

#include "../Precompiled.h"

#include "../Core/Context.h"
#include "../Core/ProcessUtils.h"
#include "../Core/Thread.h"
#include "../IO/Log.h"

#include "../DebugNew.h"

namespace Urho3D
{

TypeInfo::TypeInfo(const char* typeName, const TypeInfo* baseTypeInfo) :
    type_(typeName),
    typeName_(typeName),
    baseTypeInfo_(baseTypeInfo)
{
}

TypeInfo::~TypeInfo() = default;

bool TypeInfo::IsTypeOf(StringHash type) const
{
    const TypeInfo* current = this;
    while (current)
    {
        if (current->GetType() == type)
            return true;

        current = current->GetBaseTypeInfo();
    }

    return false;
}

bool TypeInfo::IsTypeOf(const TypeInfo* typeInfo) const
{
    if (typeInfo == nullptr)
    {
        return false;
    }

    const TypeInfo* current = this;
    while (current)
    {
        if (current->GetType() == typeInfo->GetType())
            return true;

        current = current->GetBaseTypeInfo();
    }

    return false;
}

Object::Object(Context* context) :
    context_(context),
    blockEvents_(false)
{
    assert(context_);
}

Object::~Object()
{
    UnsubscribeFromAllEvents();
    context_->RemoveEventSender(this);
}

void Object::OnEvent(Object* sender, StringHash eventType, VariantMap& eventData)
{
    if (blockEvents_)
        return;

    // Make a copy of the context pointer in case the object is destroyed during event handler invocation
    Context* context = context_;
    EventHandler* specific = nullptr;
    EventHandler* nonSpecific = nullptr;

    EventHandler* handler = eventHandlers_.First();
    while (handler)
    {
        if (handler->GetEventType() == eventType)
        {
            if (!handler->GetSender())
                nonSpecific = handler;
            else if (handler->GetSender() == sender)
            {
                specific = handler;
                break;
            }
        }
        handler = eventHandlers_.Next(handler);
    }

    // Specific event handlers have priority, so if found, invoke first
    if (specific)
    {
        context->SetEventHandler(specific);
        specific->Invoke(eventData);
        context->SetEventHandler(nullptr);
        return;
    }

    if (nonSpecific)
    {
        context->SetEventHandler(nonSpecific);
        nonSpecific->Invoke(eventData);
        context->SetEventHandler(nullptr);
    }
}

bool Object::IsInstanceOf(StringHash type) const
{
    return GetTypeInfo()->IsTypeOf(type);
}

bool Object::IsInstanceOf(const TypeInfo* typeInfo) const
{
    return GetTypeInfo()->IsTypeOf(typeInfo);
}

void Object::SubscribeToEvent(StringHash eventType, EventHandler* handler)
{
    if (!handler)
        return;

    handler->SetSenderAndEventType(nullptr, eventType);
    // Remove old event handler first
    EventHandler* previous;
    EventHandler* oldHandler = FindSpecificEventHandler(nullptr, eventType, &previous);
    if (oldHandler)
    {
        eventHandlers_.Erase(oldHandler, previous);
        eventHandlers_.InsertFront(handler);
    }
    else
    {
        eventHandlers_.InsertFront(handler);
        context_->AddEventReceiver(this, eventType);
    }
}

void Object::SubscribeToEvent(Object* sender, StringHash eventType, EventHandler* handler)
{
    // If a null sender was specified, the event can not be subscribed to. Delete the handler in that case
    if (!sender || !handler)
    {
        delete handler;
        return;
    }

    handler->SetSenderAndEventType(sender, eventType);
    // Remove old event handler first
    EventHandler* previous;
    EventHandler* oldHandler = FindSpecificEventHandler(sender, eventType, &previous);
    if (oldHandler)
    {
        eventHandlers_.Erase(oldHandler, previous);
        eventHandlers_.InsertFront(handler);
    }
    else
    {
        eventHandlers_.InsertFront(handler);
        context_->AddEventReceiver(this, sender, eventType);
    }
}

void Object::SubscribeToEvent(StringHash eventType, const std::function<void(StringHash, VariantMap&)>& function, void* userData/*=0*/)
{
    SubscribeToEvent(eventType, new EventHandler11Impl(function, userData));
}

void Object::SubscribeToEvent(Object* sender, StringHash eventType, const std::function<void(StringHash, VariantMap&)>& function, void* userData/*=0*/)
{
    SubscribeToEvent(sender, eventType, new EventHandler11Impl(function, userData));
}

void Object::UnsubscribeFromEvent(StringHash eventType)
{
    for (;;)
    {
        EventHandler* previous;
        EventHandler* handler = FindEventHandler(eventType, &previous);
        if (handler)
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), eventType);
            else
                context_->RemoveEventReceiver(this, eventType);
            eventHandlers_.Erase(handler, previous);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromEvent(Object* sender, StringHash eventType)
{
    if (!sender)
        return;

    EventHandler* previous;
    EventHandler* handler = FindSpecificEventHandler(sender, eventType, &previous);
    if (handler)
    {
        context_->RemoveEventReceiver(this, handler->GetSender(), eventType);
        eventHandlers_.Erase(handler, previous);
    }
}

void Object::UnsubscribeFromEvents(Object* sender)
{
    if (!sender)
        return;

    for (;;)
    {
        EventHandler* previous;
        EventHandler* handler = FindSpecificEventHandler(sender, &previous);
        if (handler)
        {
            context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            eventHandlers_.Erase(handler, previous);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromAllEvents()
{
    for (;;)
    {
        EventHandler* handler = eventHandlers_.First();
        if (handler)
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            else
                context_->RemoveEventReceiver(this, handler->GetEventType());
            eventHandlers_.Erase(handler);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions, bool onlyUserData)
{
    EventHandler* handler = eventHandlers_.First();
    EventHandler* previous = nullptr;

    while (handler)
    {
        EventHandler* next = eventHandlers_.Next(handler);

        if ((!onlyUserData || handler->GetUserData()) && !exceptions.Contains(handler->GetEventType()))
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            else
                context_->RemoveEventReceiver(this, handler->GetEventType());

            eventHandlers_.Erase(handler, previous);
        }
        else
            previous = handler;

        handler = next;
    }
}

void Object::SendEvent(StringHash eventType)
{
    VariantMap noEventData;

    SendEvent(eventType, noEventData);
}

void Object::SendEvent(StringHash eventType, VariantMap& eventData)
{
    if (!Thread::IsMainThread())
    {
        URHO3D_LOGERROR("Sending events is only supported from the main thread");
        return;
    }

    if (blockEvents_)
        return;

    // Make a weak pointer to self to check for destruction during event handling
    WeakPtr<Object> self(this);
    Context* context = context_;
    HashSet<Object*> processed;

    context->BeginSendEvent(this, eventType);

    // Check first the specific event receivers
    // Note: group is held alive with a shared ptr, as it may get destroyed along with the sender
    SharedPtr<EventReceiverGroup> group(context->GetEventReceivers(this, eventType));
    if (group)
    {
        group->BeginSendEvent();

        const unsigned numReceivers = group->receivers_.Size();
        for (unsigned i = 0; i < numReceivers; ++i)
        {
            Object* receiver = group->receivers_[i];
            // Holes may exist if receivers removed during send
            if (!receiver)
                continue;

            receiver->OnEvent(this, eventType, eventData);

            // If self has been destroyed as a result of event handling, exit
            if (self.Expired())
            {
                group->EndSendEvent();
                context->EndSendEvent();
                return;
            }

            processed.Insert(receiver);
        }

        group->EndSendEvent();
    }

    // Then the non-specific receivers
    group = context->GetEventReceivers(eventType);
    if (group)
    {
        group->BeginSendEvent();

        if (processed.Empty())
        {
            const unsigned numReceivers = group->receivers_.Size();
            for (unsigned i = 0; i < numReceivers; ++i)
            {
                Object* receiver = group->receivers_[i];
                if (!receiver)
                    continue;

                receiver->OnEvent(this, eventType, eventData);

                if (self.Expired())
                {
                    group->EndSendEvent();
                    context->EndSendEvent();
                    return;
                }
            }
        }
        else
        {
            // If there were specific receivers, check that the event is not sent doubly to them
            const unsigned numReceivers = group->receivers_.Size();
            for (unsigned i = 0; i < numReceivers; ++i)
            {
                Object* receiver = group->receivers_[i];
                if (!receiver || processed.Contains(receiver))
                    continue;

                receiver->OnEvent(this, eventType, eventData);

                if (self.Expired())
                {
                    group->EndSendEvent();
                    context->EndSendEvent();
                    return;
                }
            }
        }

        group->EndSendEvent();
    }

    context->EndSendEvent();
}

VariantMap& Object::GetEventDataMap() const
{
    return context_->GetEventDataMap();
}

const Variant& Object::GetGlobalVar(StringHash key) const
{
    return context_->GetGlobalVar(key);
}

const VariantMap& Object::GetGlobalVars() const
{
    return context_->GetGlobalVars();
}

void Object::SetGlobalVar(StringHash key, const Variant& value)
{
    context_->SetGlobalVar(key, value);
}

Object* Object::GetSubsystem(StringHash type) const
{
    return context_->GetSubsystem(type);
}

Object* Object::GetEventSender() const
{
    return context_->GetEventSender();
}

EventHandler* Object::GetEventHandler() const
{
    return context_->GetEventHandler();
}

bool Object::HasSubscribedToEvent(StringHash eventType) const
{
    return FindEventHandler(eventType) != nullptr;
}

bool Object::HasSubscribedToEvent(Object* sender, StringHash eventType) const
{
    if (!sender)
        return false;
    else
        return FindSpecificEventHandler(sender, eventType) != nullptr;
}

const String& Object::GetCategory() const
{
    const HashMap<String, Vector<StringHash> >& objectCategories = context_->GetObjectCategories();
    for (HashMap<String, Vector<StringHash> >::ConstIterator i = objectCategories.Begin(); i != objectCategories.End(); ++i)
    {
        if (i->second_.Contains(GetType()))
            return i->first_;
    }

    return String::EMPTY;
}

EventHandler* Object::FindEventHandler(StringHash eventType, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetEventType() == eventType)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

EventHandler* Object::FindSpecificEventHandler(Object* sender, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

EventHandler* Object::FindSpecificEventHandler(Object* sender, StringHash eventType, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender && handler->GetEventType() == eventType)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

void Object::RemoveEventSender(Object* sender)
{
    EventHandler* handler = eventHandlers_.First();
    EventHandler* previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender)
        {
            EventHandler* next = eventHandlers_.Next(handler);
            eventHandlers_.Erase(handler, previous);
            handler = next;
        }
        else
        {
            previous = handler;
            handler = eventHandlers_.Next(handler);
        }
    }
}

StringHashRegister& GetEventNameRegister()
{
    static StringHashRegister eventNameRegister(false /*non thread safe*/);
    return eventNameRegister;
}

}
<commit_msg>Stricter conditional judgment<commit_after>//
// Copyright (c) 2008-2019 the Urho3D project.
//
// 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.
//

#include "../Precompiled.h"

#include "../Core/Context.h"
#include "../Core/ProcessUtils.h"
#include "../Core/Thread.h"
#include "../IO/Log.h"

#include "../DebugNew.h"

namespace Urho3D
{

TypeInfo::TypeInfo(const char* typeName, const TypeInfo* baseTypeInfo) :
    type_(typeName),
    typeName_(typeName),
    baseTypeInfo_(baseTypeInfo)
{
}

TypeInfo::~TypeInfo() = default;

bool TypeInfo::IsTypeOf(StringHash type) const
{
    const TypeInfo* current = this;
    while (current)
    {
        if (current->GetType() == type)
            return true;

        current = current->GetBaseTypeInfo();
    }

    return false;
}

bool TypeInfo::IsTypeOf(const TypeInfo* typeInfo) const
{
    if (typeInfo == nullptr)
        return false;

    const TypeInfo* current = this;
    while (current)
    {
        if (current == typeInfo || (current->GetType() == typeInfo->GetType() && current->GetTypeName() == typeInfo->GetTypeName()))
            return true;

        current = current->GetBaseTypeInfo();
    }

    return false;
}

Object::Object(Context* context) :
    context_(context),
    blockEvents_(false)
{
    assert(context_);
}

Object::~Object()
{
    UnsubscribeFromAllEvents();
    context_->RemoveEventSender(this);
}

void Object::OnEvent(Object* sender, StringHash eventType, VariantMap& eventData)
{
    if (blockEvents_)
        return;

    // Make a copy of the context pointer in case the object is destroyed during event handler invocation
    Context* context = context_;
    EventHandler* specific = nullptr;
    EventHandler* nonSpecific = nullptr;

    EventHandler* handler = eventHandlers_.First();
    while (handler)
    {
        if (handler->GetEventType() == eventType)
        {
            if (!handler->GetSender())
                nonSpecific = handler;
            else if (handler->GetSender() == sender)
            {
                specific = handler;
                break;
            }
        }
        handler = eventHandlers_.Next(handler);
    }

    // Specific event handlers have priority, so if found, invoke first
    if (specific)
    {
        context->SetEventHandler(specific);
        specific->Invoke(eventData);
        context->SetEventHandler(nullptr);
        return;
    }

    if (nonSpecific)
    {
        context->SetEventHandler(nonSpecific);
        nonSpecific->Invoke(eventData);
        context->SetEventHandler(nullptr);
    }
}

bool Object::IsInstanceOf(StringHash type) const
{
    return GetTypeInfo()->IsTypeOf(type);
}

bool Object::IsInstanceOf(const TypeInfo* typeInfo) const
{
    return GetTypeInfo()->IsTypeOf(typeInfo);
}

void Object::SubscribeToEvent(StringHash eventType, EventHandler* handler)
{
    if (!handler)
        return;

    handler->SetSenderAndEventType(nullptr, eventType);
    // Remove old event handler first
    EventHandler* previous;
    EventHandler* oldHandler = FindSpecificEventHandler(nullptr, eventType, &previous);
    if (oldHandler)
    {
        eventHandlers_.Erase(oldHandler, previous);
        eventHandlers_.InsertFront(handler);
    }
    else
    {
        eventHandlers_.InsertFront(handler);
        context_->AddEventReceiver(this, eventType);
    }
}

void Object::SubscribeToEvent(Object* sender, StringHash eventType, EventHandler* handler)
{
    // If a null sender was specified, the event can not be subscribed to. Delete the handler in that case
    if (!sender || !handler)
    {
        delete handler;
        return;
    }

    handler->SetSenderAndEventType(sender, eventType);
    // Remove old event handler first
    EventHandler* previous;
    EventHandler* oldHandler = FindSpecificEventHandler(sender, eventType, &previous);
    if (oldHandler)
    {
        eventHandlers_.Erase(oldHandler, previous);
        eventHandlers_.InsertFront(handler);
    }
    else
    {
        eventHandlers_.InsertFront(handler);
        context_->AddEventReceiver(this, sender, eventType);
    }
}

void Object::SubscribeToEvent(StringHash eventType, const std::function<void(StringHash, VariantMap&)>& function, void* userData/*=0*/)
{
    SubscribeToEvent(eventType, new EventHandler11Impl(function, userData));
}

void Object::SubscribeToEvent(Object* sender, StringHash eventType, const std::function<void(StringHash, VariantMap&)>& function, void* userData/*=0*/)
{
    SubscribeToEvent(sender, eventType, new EventHandler11Impl(function, userData));
}

void Object::UnsubscribeFromEvent(StringHash eventType)
{
    for (;;)
    {
        EventHandler* previous;
        EventHandler* handler = FindEventHandler(eventType, &previous);
        if (handler)
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), eventType);
            else
                context_->RemoveEventReceiver(this, eventType);
            eventHandlers_.Erase(handler, previous);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromEvent(Object* sender, StringHash eventType)
{
    if (!sender)
        return;

    EventHandler* previous;
    EventHandler* handler = FindSpecificEventHandler(sender, eventType, &previous);
    if (handler)
    {
        context_->RemoveEventReceiver(this, handler->GetSender(), eventType);
        eventHandlers_.Erase(handler, previous);
    }
}

void Object::UnsubscribeFromEvents(Object* sender)
{
    if (!sender)
        return;

    for (;;)
    {
        EventHandler* previous;
        EventHandler* handler = FindSpecificEventHandler(sender, &previous);
        if (handler)
        {
            context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            eventHandlers_.Erase(handler, previous);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromAllEvents()
{
    for (;;)
    {
        EventHandler* handler = eventHandlers_.First();
        if (handler)
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            else
                context_->RemoveEventReceiver(this, handler->GetEventType());
            eventHandlers_.Erase(handler);
        }
        else
            break;
    }
}

void Object::UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions, bool onlyUserData)
{
    EventHandler* handler = eventHandlers_.First();
    EventHandler* previous = nullptr;

    while (handler)
    {
        EventHandler* next = eventHandlers_.Next(handler);

        if ((!onlyUserData || handler->GetUserData()) && !exceptions.Contains(handler->GetEventType()))
        {
            if (handler->GetSender())
                context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
            else
                context_->RemoveEventReceiver(this, handler->GetEventType());

            eventHandlers_.Erase(handler, previous);
        }
        else
            previous = handler;

        handler = next;
    }
}

void Object::SendEvent(StringHash eventType)
{
    VariantMap noEventData;

    SendEvent(eventType, noEventData);
}

void Object::SendEvent(StringHash eventType, VariantMap& eventData)
{
    if (!Thread::IsMainThread())
    {
        URHO3D_LOGERROR("Sending events is only supported from the main thread");
        return;
    }

    if (blockEvents_)
        return;

    // Make a weak pointer to self to check for destruction during event handling
    WeakPtr<Object> self(this);
    Context* context = context_;
    HashSet<Object*> processed;

    context->BeginSendEvent(this, eventType);

    // Check first the specific event receivers
    // Note: group is held alive with a shared ptr, as it may get destroyed along with the sender
    SharedPtr<EventReceiverGroup> group(context->GetEventReceivers(this, eventType));
    if (group)
    {
        group->BeginSendEvent();

        const unsigned numReceivers = group->receivers_.Size();
        for (unsigned i = 0; i < numReceivers; ++i)
        {
            Object* receiver = group->receivers_[i];
            // Holes may exist if receivers removed during send
            if (!receiver)
                continue;

            receiver->OnEvent(this, eventType, eventData);

            // If self has been destroyed as a result of event handling, exit
            if (self.Expired())
            {
                group->EndSendEvent();
                context->EndSendEvent();
                return;
            }

            processed.Insert(receiver);
        }

        group->EndSendEvent();
    }

    // Then the non-specific receivers
    group = context->GetEventReceivers(eventType);
    if (group)
    {
        group->BeginSendEvent();

        if (processed.Empty())
        {
            const unsigned numReceivers = group->receivers_.Size();
            for (unsigned i = 0; i < numReceivers; ++i)
            {
                Object* receiver = group->receivers_[i];
                if (!receiver)
                    continue;

                receiver->OnEvent(this, eventType, eventData);

                if (self.Expired())
                {
                    group->EndSendEvent();
                    context->EndSendEvent();
                    return;
                }
            }
        }
        else
        {
            // If there were specific receivers, check that the event is not sent doubly to them
            const unsigned numReceivers = group->receivers_.Size();
            for (unsigned i = 0; i < numReceivers; ++i)
            {
                Object* receiver = group->receivers_[i];
                if (!receiver || processed.Contains(receiver))
                    continue;

                receiver->OnEvent(this, eventType, eventData);

                if (self.Expired())
                {
                    group->EndSendEvent();
                    context->EndSendEvent();
                    return;
                }
            }
        }

        group->EndSendEvent();
    }

    context->EndSendEvent();
}

VariantMap& Object::GetEventDataMap() const
{
    return context_->GetEventDataMap();
}

const Variant& Object::GetGlobalVar(StringHash key) const
{
    return context_->GetGlobalVar(key);
}

const VariantMap& Object::GetGlobalVars() const
{
    return context_->GetGlobalVars();
}

void Object::SetGlobalVar(StringHash key, const Variant& value)
{
    context_->SetGlobalVar(key, value);
}

Object* Object::GetSubsystem(StringHash type) const
{
    return context_->GetSubsystem(type);
}

Object* Object::GetEventSender() const
{
    return context_->GetEventSender();
}

EventHandler* Object::GetEventHandler() const
{
    return context_->GetEventHandler();
}

bool Object::HasSubscribedToEvent(StringHash eventType) const
{
    return FindEventHandler(eventType) != nullptr;
}

bool Object::HasSubscribedToEvent(Object* sender, StringHash eventType) const
{
    if (!sender)
        return false;
    else
        return FindSpecificEventHandler(sender, eventType) != nullptr;
}

const String& Object::GetCategory() const
{
    const HashMap<String, Vector<StringHash> >& objectCategories = context_->GetObjectCategories();
    for (HashMap<String, Vector<StringHash> >::ConstIterator i = objectCategories.Begin(); i != objectCategories.End(); ++i)
    {
        if (i->second_.Contains(GetType()))
            return i->first_;
    }

    return String::EMPTY;
}

EventHandler* Object::FindEventHandler(StringHash eventType, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetEventType() == eventType)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

EventHandler* Object::FindSpecificEventHandler(Object* sender, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

EventHandler* Object::FindSpecificEventHandler(Object* sender, StringHash eventType, EventHandler** previous) const
{
    EventHandler* handler = eventHandlers_.First();
    if (previous)
        *previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender && handler->GetEventType() == eventType)
            return handler;
        if (previous)
            *previous = handler;
        handler = eventHandlers_.Next(handler);
    }

    return nullptr;
}

void Object::RemoveEventSender(Object* sender)
{
    EventHandler* handler = eventHandlers_.First();
    EventHandler* previous = nullptr;

    while (handler)
    {
        if (handler->GetSender() == sender)
        {
            EventHandler* next = eventHandlers_.Next(handler);
            eventHandlers_.Erase(handler, previous);
            handler = next;
        }
        else
        {
            previous = handler;
            handler = eventHandlers_.Next(handler);
        }
    }
}

StringHashRegister& GetEventNameRegister()
{
    static StringHashRegister eventNameRegister(false /*non thread safe*/);
    return eventNameRegister;
}

}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2014 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#pragma once

namespace transport {

namespace messages {

class result_message;

}

}

#include "transport/event.hh"

#include "cql3/statements/cf_statement.hh"
#include "cql3/cql_statement.hh"

#include "core/shared_ptr.hh"

#include <experimental/optional>

namespace cql3 {

namespace statements {

namespace messages = transport::messages;

/**
 * Abstract class for statements that alter the schema.
 */
class schema_altering_statement : public cf_statement, public virtual cql_statement, public ::enable_shared_from_this<schema_altering_statement> {
private:
    const bool _is_column_family_level;

protected:
    schema_altering_statement()
        : cf_statement{::shared_ptr<cf_name>{}}
        , _is_column_family_level{false}
    { }

    schema_altering_statement(::shared_ptr<cf_name> name)
        : cf_statement{std::move(name)}
        , _is_column_family_level{true}
    { }

    virtual bool uses_function(const sstring& ks_name, const sstring& function_name) const override {
        return cf_statement::uses_function(ks_name, function_name);
    }

    virtual uint32_t get_bound_terms() override {
        return 0;
    }

    virtual void prepare_keyspace(service::client_state& state) override {
        if (_is_column_family_level) {
            cf_statement::prepare_keyspace(state);
        }
    }

    virtual ::shared_ptr<prepared> prepare(database& db) override {
        return ::make_shared<parsed_statement::prepared>(this->shared_from_this());
    }

    virtual shared_ptr<transport::event::schema_change> change_event() = 0;

    /**
     * Announces the migration to other nodes in the cluster.
     * @return true if the execution of this statement resulted in a schema change, false otherwise (when IF NOT EXISTS
     * is used, for example)
     * @throws RequestValidationException
     */
    virtual bool announce_migration(bool is_local_only) = 0;

    virtual future<::shared_ptr<messages::result_message>>
    execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) override;

    virtual future<::shared_ptr<messages::result_message>>
    execute_internal(database& db, service::query_state& state, const query_options& options) override;
};

}

}
<commit_msg>cql3: Drop virtual inheritance from schema_altering_statement<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2014 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#pragma once

namespace transport {

namespace messages {

class result_message;

}

}

#include "transport/event.hh"

#include "cql3/statements/cf_statement.hh"
#include "cql3/cql_statement.hh"

#include "core/shared_ptr.hh"

#include <experimental/optional>

namespace cql3 {

namespace statements {

namespace messages = transport::messages;

/**
 * Abstract class for statements that alter the schema.
 */
class schema_altering_statement : public cf_statement, public cql_statement, public ::enable_shared_from_this<schema_altering_statement> {
private:
    const bool _is_column_family_level;

protected:
    schema_altering_statement()
        : cf_statement{::shared_ptr<cf_name>{}}
        , _is_column_family_level{false}
    { }

    schema_altering_statement(::shared_ptr<cf_name> name)
        : cf_statement{std::move(name)}
        , _is_column_family_level{true}
    { }

    virtual bool uses_function(const sstring& ks_name, const sstring& function_name) const override {
        return cf_statement::uses_function(ks_name, function_name);
    }

    virtual uint32_t get_bound_terms() override {
        return 0;
    }

    virtual void prepare_keyspace(service::client_state& state) override {
        if (_is_column_family_level) {
            cf_statement::prepare_keyspace(state);
        }
    }

    virtual ::shared_ptr<prepared> prepare(database& db) override {
        return ::make_shared<parsed_statement::prepared>(this->shared_from_this());
    }

    virtual shared_ptr<transport::event::schema_change> change_event() = 0;

    /**
     * Announces the migration to other nodes in the cluster.
     * @return true if the execution of this statement resulted in a schema change, false otherwise (when IF NOT EXISTS
     * is used, for example)
     * @throws RequestValidationException
     */
    virtual bool announce_migration(bool is_local_only) = 0;

    virtual future<::shared_ptr<messages::result_message>>
    execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) override;

    virtual future<::shared_ptr<messages::result_message>>
    execute_internal(database& db, service::query_state& state, const query_options& options) override;
};

}

}
<|endoftext|>