#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell
###############################################################################
# Sanity check plugin for the Krazy project.                                  #
# Copyright (C) 2007-2010 by Allen Winter <winter@kde.org>                    #
#                                                                             #
# This program 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.,     #
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.               #
#                                                                             #
###############################################################################

# Tests KDE source for prohibited Qt Classes

# Program options:
#   --help:          print one-line help message and exit
#   --version:       print one-line version information and exit
#   --priority:      report issues of the specified priority only
#   --strict:        report issues with the specified strictness level only
#   --explain:       print an explanation with solving instructions
#   --installed      file is to be installed
#   --quiet:         suppress all output messages
#   --verbose:       print the offending content

# Exits with status=0 if test condition is not present in the source;
# else exits with the number of failures encountered.

use strict;
use Cwd 'abs_path';
use FindBin qw($Bin);
use lib "$Bin/../../../../lib";
use Krazy::PreProcess;
use Krazy::Utils;

my($Prog) = "qclasses";
my($Version) = "1.11";

&parseArgs();

&Help() if &helpArg();
&Version() if &versionArg();
&Explain() if &explainArg();
if ($#ARGV != 0){ &Help(); Exit 0; }

my($f) = $ARGV[0];
my($absf) = abs_path($f);

my($filetype) = fileType($f);

# open file and slurp it in (C++, non-headers only)
if ($absf !~ m+/kdesupport/+ &&
    ($filetype eq "c++" || $filetype eq "designer")) {
  open(F, "$f") || die "Couldn't open $f";
} else {
  print "okay\n" if (!&quietArg());
  Exit 0;
}
my(@data_lines) = <F>;
close(F);

# Remove C-style comments and #if 0 blocks from the file input
my(@lines);
if ($filetype eq "c++") {
  @lines = RemoveIfZeroBlockC( RemoveCommentsC( @data_lines ) );
} else {
  @lines = @data_lines;
}

my($cnt) = 0;
my($linecnt) = 0;
my($lstr) = "";

my($line);
while ($linecnt < $#lines) {
  $line = $lines[$linecnt++];
  if ($filetype eq "c++") {
    if ($line =~ m+//.*[Kk]razy:excludeall=.*$Prog+ ||
	$line =~ m+//.*[Kk]razy:skip+) {
      $cnt = 0;
      last;
    }
    next if ($line =~ m+//.*[Kk]razy:exclude=.*$Prog+);
    $line =~ s+//.*++;  #skip C++ comments
  }

  # Don't use Qt 4 classes that got deprecated.
  &doIt($line,"QHttp","KIO::AccessManager",'');
  &doIt($line,"QWorkspace","QMdiArea",'');

  # use KDE classes instead of Qt classes where possible
  &doIt($line,"QColorDialog","KColorDialog",'');
  &doIt($line,"QComboBox","KComboBox",'');
  &doIt($line,"QMessageBox","KMessageBox",'');
  &doIt($line,"QErrorMessage","KMessageBox",'');
  &doIt($line,"QInputDialog","KInputDialog",'');
  &doIt($line,"QFileDialog","KFileDialog",'');
  &doIt($line,"QProgressDialog","KProgressDialog",'');
  &doIt($line,"QSplashScreen","KSplashScreen",'');
  &doIt($line,"QSystemTrayIcon","KNotificationItem",'');
  &doIt($line,"QDialog","KDialog",'');
  &doIt($line,"QLineEdit","KLineEdit",'');
  &doIt($line,"QTabBar","KTabBar",'');
  &doIt($line,"QTabWidget","KTabWidget",'');
  &doIt($line,"QTextBrowser","KTextBrowser",'');
  &doIt($line,"QTextEdit","KTextEdit",'');
  &doIt($line,"QUrl","KUrl",'');
  &doIt($line,"QNetworkAcessManager","KIO::AccessManager",'');
}

if (!$cnt) {
  print "okay\n" if (!&quietArg());
  Exit 0;
} else {
  $lstr =~ s/,$//;
  print "$lstr ($cnt)\n" if (!&quietArg());
  Exit $cnt;
}

sub doIt() {
  my($line,$qname,$kname,$nregexp) = @_;
  if ($f !~ m/$kname\.c/i && &qClass($line,$qname,$nregexp)) {
    $cnt++;
    $lstr .= $qname . "[$kname],";
    print "=> $line\n" if (&verboseArg());
  }
}

sub qClass() {
  my($l,$qname,$nregexp) = @_;

  if ($filetype eq "c++") {
    if ($l =~ m/new\s+$qname\s*\(/ ||
	$l =~ m/$qname\::[[:alnum:]]+[[:space:]]*\(/ ||
	$l =~ m/$qname[[:space:]]*\*[[:space:]]*[[:alnum:]]+/ ||
	$l =~ m/[:,]\s*public\s+$qname/ ||
	(($l =~ m/=[[:space:]]*$qname\::/ || $l =~ m/=[[:space:]]*$qname[[:space:]]*\(/) && ($l !~ m/==/ && $l !~ m/\!=/))) {
      return 0 if ($nregexp ne "" && $l =~ m/$nregexp/);
      return 1 if (!&whiteList($l));
    }
  }

  if ($filetype eq "designer") {
    if ($l =~ m/class=\"$qname\"/) {
      return 0 if ($nregexp ne "" && $l =~ m/$nregexp/);
      return 1 if (!&whiteList($l));
    }
  }

  return 0;
}

sub whiteList {
  my($l) = @_;
  if ($l =~ m/QUrl::toPercentEncoding/ ||
      $l =~ m/QUrl::fromPercentEncoding/ ||
      $l =~ m/QUrl::toAce/ ||
      $l =~ m/QUrl::fromAce/ ||
      $l =~ m/QLineEdit::dropEvent/ ||
      $l =~ m/QDialog::DialogCode/ ||
      $l =~ m/QDialog::accept/ ||
      $l =~ m/QDialog::Accepted/ ||
      $l =~ m/QDialog::reject/ ||
      $l =~ m/QDialog::Rejected/ ||
      $l =~ m/[:,]\s*public\s+QDialog/ ||
      $l =~ m/class=\"QDialog\"/ ||
      $l =~ m/KServiceTypeTrader::createInstanceFromQuery<.*>/) {
    return 1;
  } else {
    return 0;
  }
}

sub Help {
  print "Check for Qt classes that should not be used\n";
  Exit 0 if &helpArg();
}

sub Version {
  print "$Prog, version $Version\n";
  Exit 0 if &versionArg();
}

sub Explain {
  print "Don't use Qt 4 classes that are deprecated. Additionally make sure to use the KDE version of some Qt GUI elements to provide a consistent look and feel for the KDE desktop. The KDE classes are not just adding functionalities to the Qt base class and are mostly not even based on the Qt class. <http://techbase.kde.org/Policies/API_to_Avoid> *Please* *refer* *to* *the* *API* *documentation* *for* *details* *before* *porting* *to* *the* *K* *classes*.\n";
  Exit 0 if &explainArg();
}
