diff --git a/bin/tests/scenarios/fancy_argument_types_matching_issue/merge.java b/bin/tests/scenarios/fancy_argument_types_matching_issue/merge.java index fcd3bc4..229dfeb 100644 --- a/bin/tests/scenarios/fancy_argument_types_matching_issue/merge.java +++ b/bin/tests/scenarios/fancy_argument_types_matching_issue/merge.java @@ -1 +1 @@ -package de . fosd . jdime . common ; import de . fosd . jdime . common . operations . AddOperation ; import AST . * ; import de . fosd . jdime . common . operations . ConflictOperation ; public class ASTNodeArtifact extends Artifact < ASTNodeArtifact > { private ASTNodeArtifact ( final ASTNode < ? > astnode ) { assert ( astnode != null ) ; this . astnode = astnode ; this . initializeChildren ( ) ; } public ASTNodeArtifact ( final FileArtifact artifact ) { assert ( artifact != null ) ; setRevision ( artifact . getRevision ( ) ) ; ASTNode < ? > astnode ; if ( artifact . isEmpty ( ) ) { astnode = new ASTNode < > ( ) ; } else { Program p = initProgram ( ) ; p . addSourceFile ( artifact . getPath ( ) ) ; astnode = p ; } this . astnode = astnode ; this . initializeChildren ( ) ; renumberTree ( ) ; } } \ No newline at end of file +package de . fosd . jdime . common ; import AST . * ; import de . fosd . jdime . common . operations . ConflictOperation ; import de . fosd . jdime . common . operations . AddOperation ; public class ASTNodeArtifact extends Artifact < ASTNodeArtifact > { private ASTNodeArtifact ( final ASTNode < ? > astnode ) { assert ( astnode != null ) ; this . astnode = astnode ; this . initializeChildren ( ) ; } public ASTNodeArtifact ( final FileArtifact artifact ) { assert ( artifact != null ) ; setRevision ( artifact . getRevision ( ) ) ; ASTNode < ? > astnode ; if ( artifact . isEmpty ( ) ) { astnode = new ASTNode < > ( ) ; } else { Program p = initProgram ( ) ; p . addSourceFile ( artifact . getPath ( ) ) ; astnode = p ; } this . astnode = astnode ; this . initializeChildren ( ) ; renumberTree ( ) ; } } \ No newline at end of file diff --git a/bin/tests/scenarios/import_declarations_grouping_node/base.java b/bin/tests/scenarios/import_declarations_grouping_node/base.java new file mode 100644 index 0000000..dca1ec7 --- /dev/null +++ b/bin/tests/scenarios/import_declarations_grouping_node/base.java @@ -0,0 +1,14 @@ +package br.fosd.jdime.stats; + +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.TreeSet; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +import de.fosd.jdime.common.LangElem; + +public class ASTStats { +} diff --git a/bin/tests/scenarios/import_declarations_grouping_node/left.java b/bin/tests/scenarios/import_declarations_grouping_node/left.java new file mode 100644 index 0000000..94f43dc --- /dev/null +++ b/bin/tests/scenarios/import_declarations_grouping_node/left.java @@ -0,0 +1,13 @@ +package br.fosd.jdime.stats; + +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.TreeSet; + +import de.fosd.jdime.common.LangElem; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +public class ASTStats { +} diff --git a/bin/tests/scenarios/import_declarations_grouping_node/merge.java b/bin/tests/scenarios/import_declarations_grouping_node/merge.java new file mode 100644 index 0000000..1fa543b --- /dev/null +++ b/bin/tests/scenarios/import_declarations_grouping_node/merge.java @@ -0,0 +1 @@ +package br . fosd . jdime . stats ; import java . text . DecimalFormat ; import java . util . HashMap ; import java . util . TreeSet ; import de . fosd . jdime . common . LangElem ; import java . util . logging . Level ; import java . util . logging . Logger ; public class ASTStats { } \ No newline at end of file diff --git a/bin/tests/scenarios/import_declarations_grouping_node/right.java b/bin/tests/scenarios/import_declarations_grouping_node/right.java new file mode 100644 index 0000000..0ad5c79 --- /dev/null +++ b/bin/tests/scenarios/import_declarations_grouping_node/right.java @@ -0,0 +1,12 @@ +package br.fosd.jdime.stats; + +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.fosd.jdime.common.LangElem; + +public class ASTStats { +} diff --git a/bin/tests/scenarios/imports_are_merged_correctly/merge.java b/bin/tests/scenarios/imports_are_merged_correctly/merge.java index bf12df3..51b0a11 100644 --- a/bin/tests/scenarios/imports_are_merged_correctly/merge.java +++ b/bin/tests/scenarios/imports_are_merged_correctly/merge.java @@ -1 +1 @@ -package de . fosd . jdime . merge ; import java . util . List ; import AST . * ; import de . fosd . jdime . operations . AddOperation ; import de . fosd . jdime . operations . ConflictOperation ; import de . fosd . jdime . operations . MergeOperation ; import static de . fosd . jdime . artifact . Artifacts . root ; import static de . fosd . jdime . strdump . DumpMode . PLAINTEXT_TREE ; \ No newline at end of file +package de . fosd . jdime . merge ; import java . util . List ; import AST . * ; import de . fosd . jdime . operations . AddOperation ; import de . fosd . jdime . operations . ConflictOperation ; import de . fosd . jdime . operations . MergeOperation ; import static de . fosd . jdime . artifact . Artifacts . root ; import static de . fosd . jdime . strdump . DumpMode . PLAINTEXT_TREE ; \ No newline at end of file diff --git a/bin/tests/scenarios/jdime_matching_issue/merge.java b/bin/tests/scenarios/jdime_matching_issue/merge.java index fca08b5..fece629 100644 --- a/bin/tests/scenarios/jdime_matching_issue/merge.java +++ b/bin/tests/scenarios/jdime_matching_issue/merge.java @@ -1 +1 @@ - package de . fosd . jdime . artifact ; import java . security . MessageDigest ; public abstract class Artifact < T extends Artifact < T > > implements Comparable < T > , StatisticsInterface { public boolean hasChanges ( Revision revision ) { if ( this . revision . equals ( revision ) ) { return false ; } if ( ! hasMatching ( revision ) ) { return true ; } T match = getMatching ( revision ) . getMatchingArtifact ( this ) ; return getTreeSize ( ) != match . getTreeSize ( ) || ! getTreeHash ( ) . equals ( match . getTreeHash ( ) ) ; } } \ No newline at end of file +package de . fosd . jdime . artifact ; import java . security . MessageDigest ; public abstract class Artifact < T extends Artifact < T > > implements Comparable < T > , StatisticsInterface { public boolean hasChanges ( Revision revision ) { if ( this . revision . equals ( revision ) ) { return false ; } if ( ! hasMatching ( revision ) ) { return true ; } T match = getMatching ( revision ) . getMatchingArtifact ( this ) ; return getTreeSize ( ) != match . getTreeSize ( ) || ! getTreeHash ( ) . equals ( match . getTreeHash ( ) ) ; } } \ No newline at end of file diff --git a/bin/tests/scenarios/overload_methods_with_spread_parameter/merge.java b/bin/tests/scenarios/overload_methods_with_spread_parameter/merge.java index 9e34e90..0ec2e12 100644 --- a/bin/tests/scenarios/overload_methods_with_spread_parameter/merge.java +++ b/bin/tests/scenarios/overload_methods_with_spread_parameter/merge.java @@ -1 +1 @@ - package de . fosd . jdime ; import java . io . File ; import java . net . URISyntaxException ; import java . net . URL ; import java . util . Arrays ; import org . junit . BeforeClass ; import static org . junit . Assert . assertNotNull ; import static org . junit . Assert . assertTrue ; import static org . junit . Assert . fail ; public class JDimeTest { protected static File file ( File parent , String child ) { File f = new File ( parent , child ) ; assertTrue ( f + " does not exist." , f . exists ( ) ) ; return f ; } protected static File file ( File parent , String name , String ... names ) { if ( names != null ) { String path = String . format ( "%s/%s" , name , String . join ( "/" , names ) ) ; return file ( parent , path ) ; } else { return file ( parent , name ) ; } } protected static File file ( String path ) { URL res = JDimeTest . class . getResource ( path ) ; assertNotNull ( "The file " + path + " was not found." , res ) ; try { return new File ( res . toURI ( ) ) ; } catch ( URISyntaxException e ) { fail ( e . getMessage ( ) ) ; return null ; } } protected static File file ( String name , String ... names ) { if ( names != null ) { String path = String . format ( "/%s/%s" , name , String . join ( "/" , names ) ) ; return file ( path ) ; } else { return file ( "/" + name ) ; } } } \ No newline at end of file +package de . fosd . jdime ; import java . io . File ; import java . net . URISyntaxException ; import java . net . URL ; import java . util . Arrays ; import org . junit . BeforeClass ; import static org . junit . Assert . assertNotNull ; import static org . junit . Assert . assertTrue ; import static org . junit . Assert . fail ; public class JDimeTest { protected static File file ( File parent , String child ) { File f = new File ( parent , child ) ; assertTrue ( f + " does not exist." , f . exists ( ) ) ; return f ; } protected static File file ( File parent , String name , String ... names ) { if ( names != null ) { String path = String . format ( "%s/%s" , name , String . join ( "/" , names ) ) ; return file ( parent , path ) ; } else { return file ( parent , name ) ; } } protected static File file ( String path ) { URL res = JDimeTest . class . getResource ( path ) ; assertNotNull ( "The file " + path + " was not found." , res ) ; try { return new File ( res . toURI ( ) ) ; } catch ( URISyntaxException e ) { fail ( e . getMessage ( ) ) ; return null ; } } protected static File file ( String name , String ... names ) { if ( names != null ) { String path = String . format ( "/%s/%s" , name , String . join ( "/" , names ) ) ; return file ( path ) ; } else { return file ( "/" + name ) ; } } } \ No newline at end of file diff --git a/model/src/cst_node.rs b/model/src/cst_node.rs index f6d850a..e8c99f5 100644 --- a/model/src/cst_node.rs +++ b/model/src/cst_node.rs @@ -34,6 +34,20 @@ impl CSTNode<'_> { CSTNode::NonTerminal(node) => node.contents(), } } + + pub fn start_position(&self) -> Point { + match self { + CSTNode::Terminal(node) => node.start_position, + CSTNode::NonTerminal(node) => node.start_position, + } + } + + pub fn end_position(&self) -> Point { + match self { + CSTNode::Terminal(node) => node.end_position, + CSTNode::NonTerminal(node) => node.end_position, + } + } } #[derive(Debug, Default, Clone)] diff --git a/parsing/src/parse.rs b/parsing/src/parse.rs index 4ca0f0d..1768994 100644 --- a/parsing/src/parse.rs +++ b/parsing/src/parse.rs @@ -43,6 +43,73 @@ fn explore_node<'a>(node: Node, src: &'a str, config: &'a ParserConfiguration) - } } +fn tweak_import_declarations(root: CSTNode<'_>) -> CSTNode<'_> { + match root.kind() { + "program" => match root { + CSTNode::Terminal(_) => root.to_owned(), + CSTNode::NonTerminal(program) => { + let import_declaration_children: Vec = program + .children + .iter() + .filter(|node| node.kind() == "import_declaration") + .cloned() + .collect(); + + if import_declaration_children.is_empty() { + return CSTNode::NonTerminal(program); + } + + let import_declarations_start = import_declaration_children + .first() + .unwrap() + .start_position(); + + let import_declarations_end = + import_declaration_children.last().unwrap().end_position(); + + let import_declarations = CSTNode::NonTerminal(NonTerminal { + id: uuid::Uuid::new_v4(), + kind: "import_declarations", + children: import_declaration_children, + start_position: import_declarations_start, + end_position: import_declarations_end, + are_children_unordered: true, + }); + + let first_import_declaration_index = program + .children + .iter() + .position(|node| node.kind() == "import_declaration") + .unwrap(); + let last_import_declaration_index = program + .children + .iter() + .rposition(|node| node.kind() == "import_declaration") + .unwrap(); + + let mut new_program_children: Vec> = vec![]; + new_program_children.extend_from_slice( + &program.children.iter().as_slice()[..first_import_declaration_index], + ); + new_program_children.push(import_declarations); + new_program_children.extend_from_slice( + &program.children.iter().as_slice()[last_import_declaration_index + 1..], + ); + + CSTNode::NonTerminal(NonTerminal { + id: program.id, + kind: program.kind, + start_position: program.start_position, + end_position: program.end_position, + children: new_program_children, + are_children_unordered: false, + }) + } + }, + _ => root.to_owned(), + } +} + pub fn parse_string<'a>( src: &'a str, config: &'a ParserConfiguration, @@ -52,9 +119,9 @@ pub fn parse_string<'a>( .set_language(config.language) .map_err(|_| "There was an error while setting the parser language")?; - let parsed = parser.parse(src, None); - match parsed { - Some(parsed) => Result::Ok(explore_node(parsed.root_node(), src, config)), - None => Result::Err("It was not possible to parse the tree."), - } + let parsed = parser + .parse(src, None) + .ok_or("It was not possible to parse the tree.")?; + let root = explore_node(parsed.root_node(), src, config); + Ok(tweak_import_declarations(root)) }