package QML::File;
use strict;
use warnings;
use IO::File;
use base qw(Class::Accessor);
our $VERSION = 0.01;
QML::File->mk_ro_accessors(qw(name objectType id));
sub new {
my ( $class, $file ) = @_;
my $fileHandle = IO::File->new( $file, "r" );
my ($name) = $file =~ /([^\.\\\/]+)\.qml$/;
my $self = bless {
fileHandle => $fileHandle,
name => $name,
imports => [],
objectType => '',
id => {},
propertyDeclarations => [],
signalDeclarations => [],
javaScriptFunctions => [],
objectProperties => [],
childObjects => []
}, $class;
$self->_parse;
return $self;
}
sub _parse {
my ($self) = @_;
my $isInComment;
my $isInFunction;
my $isInChildObject;
my $braceLevel = 0;
my $lineNum = 0;
if ( !$self->{fileHandle} ) {
print "ERROR: Could not open $self->{name}\n";
return;
}
my @lines = $self->{fileHandle}->getlines;
foreach my $line (@lines) {
$lineNum++;
# Trim leading and trailing whitespace
$line =~ s/^\s+//;
$line =~ s/\s+$//;
# Comment
next if ( $line =~ /^\/\// );
# Long Comment
$isInComment = 1 if ( $line =~ /\/\*/ );
if ($isInComment) {
$isInComment = 0 if ( $line =~ /\*\// );
next;
}
# FileSystemImport
my ($filename) = $line =~ /import\s+"([^"]+)"/;
if ( defined $filename ) {
push @{ $self->{imports} },
{ filename => $filename, line => $line, lineNum => $lineNum };
}
# LibraryImport
my ($importId) = $line =~ /import\s+([\w\d_\.]+)/;
if ( defined $importId ) {
push @{ $self->{imports} },
{ name => $importId, line => $line, lineNum => $lineNum };
}
# ObjectDeclaration
if ( !$self->{objectType} ) {
my ($type) = $line =~ /^([\w\d_\.]+)/;
if ( defined $type && $type ne 'import' ) {
$self->{objectType} =
{ name => $type, line => $line, lineNum => $lineNum };
}
}
if ( $braceLevel == 1 ) {
# PropertyDeclaration
my ( $propertyType, $identifier ) =
$line =~ /^property\s+([\w\d_]+)\s+([\w\d_]+)/;
if ( defined $propertyType ) {
push @{ $self->{propertyDeclarations} },
{
type => $propertyType,
name => $identifier,
line => $line,
lineNum => $lineNum
};
}
# SignalDeclaration
my ($signalName) = $line =~ /^signal\s+([\w\d_]+)/;
if ( defined $signalName ) {
push @{ $self->{signalDeclarations} },
{ name => $signalName, line => $line, lineNum => $lineNum };
}
# JavaScriptFunctions
my ($functionName) = $line =~ /^function\s+([\w\d_]+)/;
if ( defined $functionName ) {
push @{ $self->{javaScriptFunctions} },
{ name => $functionName, line => $line, lineNum => $lineNum };
$isInFunction = 1;
}
# ObjectProperties
my ($propertyName) = $line =~ /^([\w\d_\.]+)\s*:/;
if ( defined $propertyName ) {
push @{ $self->{objectProperties} },
{ name => $propertyName, line => $line, lineNum => $lineNum };
# ID
if ( !defined $self->{id}->{name} ) {
my ($id) = $line =~ /^id\s*:\s*([\w\d_\.]+)/;
if ( defined $id ) {
$self->{id} =
{ name => $id, line => $line, lineNum => $lineNum };
}
}
}
# Child objects
my ($childType) = $line =~ /^([\w\d_\.]+)\s*\{/;
if ( defined $childType ) {
next if $childType =~ /^anchors|font$/;
push @{ $self->{childObjects} },
{ type => $childType, line => $line, lineNum => $lineNum };
}
}
# Curly braces
$braceLevel += ( $line =~ tr/\{// );
$braceLevel -= ( $line =~ tr/\}// );
}
}
sub imports {
my ($self) = @_;
return @{ $self->{imports} };
}
sub propertyDeclarations {
my ($self) = @_;
return @{ $self->{propertyDeclarations} };
}
sub signalDeclarations {
my ($self) = @_;
return @{ $self->{signalDeclarations} };
}
sub javaScriptFunctions {
my ($self) = @_;
return @{ $self->{javaScriptFunctions} };
}
sub objectProperties {
my ($self) = @_;
return @{ $self->{objectProperties} };
}
sub childObjects {
my ($self) = @_;
return @{ $self->{childObjects} };
}
1;
__END__
=head1 NAME
QML::File - Basic parsing of the high-level structure of QML files.
=head1 SYNOPSIS
use QML::File
my $parser = new QML::File('main.qml');
my @imports = $parser->imports;
foreach my $import (@imports) {
print "Found import of $import->{name} on line $import->{lineNum}\n";
}
my $objectType = $parser->objectType;
my $id = $parser->id
my @properties = $parser->propertyDeclarations;
my @signals = $parser->signalDeclarations;
my @functions = $parser->javaScriptFunctions;
my @objectProperties = $parser->objectProperties;
my @childObjects = $parser->childObjects;
=head1 DESCRIPTION
This module parses QML files at a very high level, allowing you to
determine basic information like the signals, properties, functions,
and child objects defined in the QML file.
=head1 CONSTRUCTOR
=head2 new
use QML::File;
my $file = new QML::File('Filename.qml');
Returns a newly created C<QML::File> object for the specified QML file.
=head1 METHODS
=head2 childObjects
Returns a list of child objects. Each element in the returned array is a hash of the form:
my $child = {type => $childType, line => $line, lineNum => $lineNum};
=head2 id
Returns the ID of the main component.
=head2 imports
my @imports = $parser->imports;
Returns the list of imports defined in the file. Each element in the returned array is a hash of the form:
my $fileImport = {filename => $filename, line => $line, lineNum => $lineNum};
or
my $namedImport = {name => $name, line => $line, lineNum => $lineNum};
depending on the type of import.
=head2 javaScriptFunctions
Returns an array of Javascript functions defined in the QML component. Each element in the returned array is a hash of the form:
my $function = {name => $functionName, line => $line, lineNum => $lineNum};
=head2 name
Returns the name of the QML component defined by the QML file.
=head2 objectProperties
Returns an array of object properties assigned. Each element in the returned array is a hash of the form:
my $objectProperty = {name => $name, line => $line, lineNum => $lineNum};
=head2 objectType
Returns the type of the main component defined in the QML file.
=head2 propertyDeclarations
Returns an array of property declarations. Each element in the returned array is a hash of the form:
my $property = {name => $name, line => $line, lineNum => $lineNum};
=head2 signalDeclarations
Returns an array of signal declarations. Each element in the returned array is a hash of the form:
my $signal = {name => $name, line => $line, lineNum => $lineNum};
=head1 SEE ALSO
C<check_qml.pl> - Checks a QML file for coding conventions described at L<http://doc.qt.digia.com/qt/qml-coding-conventions.html>
=head1 AUTHOR
Zachary Blair, E<lt>zblair@cpan.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2013 by Zachary Blair
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.8 or,
at your option, any later version of Perl 5 you may have available.
=cut