@@ -81,17 +81,22 @@ public CheckAlignmentCommand() {
8181
8282 options .addOption ("r" , "reasoner" , true , "the reasoner to use" );
8383 options .addOption ("O" , "report-output" , true , "write report to the specified file" );
84+ options .addOption (null , "detail" , true , "what to report (ROOT / BASE-ROOT / ALL)" );
8485 options .addOption ("x" , "fail" , true , "if true (default), fail if the ontology is misaligned" );
8586 }
8687
8788 @ Override
8889 public void performOperation (CommandState state , CommandLine line ) throws Exception {
8990 boolean ignoreDangling = CommandLineHelper .getBooleanValue (line , "ignore-dangling" , false );
9091 boolean failOnError = CommandLineHelper .getBooleanValue (line , "fail" , true );
92+ ReportMode reportMode = ReportMode .fromString (CommandLineHelper .getDefaultValue (line , "detail" , "root" ));
9193 if ( line .hasOption ("base-iri" ) ) {
9294 for ( String iri : line .getOptionValues ("base-iri" ) ) {
9395 basePrefixes .add (getIRI (iri , "base-iri" ).toString ());
9496 }
97+ } else if ( reportMode == ReportMode .BASE_ROOT ) {
98+ // --detail BASE-ROOT makes no sense without a base IRI
99+ reportMode = ReportMode .GLOBAL_ROOT ;
95100 }
96101 factory = state .getOntology ().getOWLOntologyManager ().getOWLDataFactory ();
97102
@@ -130,14 +135,14 @@ public void performOperation(CommandState state, CommandLine line) throws Except
130135 }
131136
132137 OWLReasoner reasoner = CommandLineHelper .getReasonerFactory (line ).createReasoner (ontology );
133- Set <OWLClass > unalignedClasses = getUnalignedClasses (ontology , reasoner , upperClasses , ignoreDangling );
138+ Set <OWLClass > unalignedClasses = getAllUnalignedClasses (ontology , reasoner , upperClasses , ignoreDangling );
134139
135140 if ( line .hasOption ("report-output" ) ) {
136141 // If a report has been requested, we always produce it, even if no unaligned
137142 // classes were found
138143 BufferedWriter writer = new BufferedWriter (new FileWriter (line .getOptionValue ("report-output" )));
139144 List <String > unalignedIRIs = new ArrayList <>();
140- for ( OWLClass unalignedClass : unalignedClasses ) {
145+ for ( OWLClass unalignedClass : trimReport ( reasoner , unalignedClasses , reportMode ) ) {
141146 unalignedIRIs .add (unalignedClass .getIRI ().toString ());
142147 }
143148 unalignedIRIs .sort ((a , b ) -> a .compareTo (b ));
@@ -149,7 +154,7 @@ public void performOperation(CommandState state, CommandLine line) throws Except
149154 }
150155
151156 if ( !unalignedClasses .isEmpty () ) {
152- logger .error ("Ontology contains {} top-level unaligned class(es)" , unalignedClasses .size ());
157+ logger .error ("Ontology contains {} unaligned class(es)" , unalignedClasses .size ());
153158 if ( failOnError ) {
154159 System .exit (1 );
155160 }
@@ -191,10 +196,7 @@ private Set<OWLClass> getPreferredRoots(OWLOntology ontology) {
191196 return roots ;
192197 }
193198
194- /*
195- * Gets all top-level unaligned classes in the ontology.
196- */
197- private Set <OWLClass > getUnalignedClasses (OWLOntology ontology , OWLReasoner reasoner , Set <OWLClass > upperClasses ,
199+ private Set <OWLClass > getAllUnalignedClasses (OWLOntology ontology , OWLReasoner reasoner , Set <OWLClass > upperClasses ,
198200 boolean ignoreDangling ) {
199201 Set <OWLClass > unalignedClasses = new HashSet <>();
200202 for ( OWLClass klass : ontology .getClassesInSignature (Imports .INCLUDED ) ) {
@@ -205,6 +207,10 @@ private Set<OWLClass> getUnalignedClasses(OWLOntology ontology, OWLReasoner reas
205207 if ( Util .isObsolete (ontology , klass ) ) {
206208 continue ;
207209 }
210+ if ( unalignedClasses .contains (klass ) ) {
211+ // Already found, skip
212+ continue ;
213+ }
208214
209215 Set <OWLClass > ancestors = reasoner .getSuperClasses (klass , false ).getFlattened ();
210216 boolean aligned = false ;
@@ -214,28 +220,49 @@ private Set<OWLClass> getUnalignedClasses(OWLOntology ontology, OWLReasoner reas
214220 break ;
215221 }
216222 }
223+
217224 if ( !aligned ) {
218- if ( ancestors .size () == 1 ) {
219- // This is already a top-level class
220- logger .debug ("Unaligned class: {}" , klass .getIRI ().toQuotedString ());
221- unalignedClasses .add (klass );
222- } else {
223- // Find the top-level ancestor(s)
224- for ( OWLClass ancestor : ancestors ) {
225- if ( reasoner .getSuperClasses (ancestor , false ).isTopSingleton () ) {
226- logger .debug ("Unaligned class: {} (from {})" , ancestor .getIRI ().toQuotedString (),
227- klass .getIRI ().toQuotedString ());
228- unalignedClasses .add (ancestor );
229- }
230- }
231- }
225+ logger .debug ("Unaligned class: {}" , klass .getIRI ().toQuotedString ());
226+ unalignedClasses .add (klass );
227+ // If this class is unaligned, then all its ancestors necessarily are too
228+ unalignedClasses .addAll (ancestors );
232229 }
233230 }
234231 }
235232
233+ unalignedClasses .remove (factory .getOWLThing ());
236234 return unalignedClasses ;
237235 }
238236
237+ private Set <OWLClass > trimReport (OWLReasoner reasoner , Set <OWLClass > unalignedClasses , ReportMode mode ) {
238+ Set <OWLClass > trimmedOut = new HashSet <>();
239+ Set <OWLClass > reportSet = new HashSet <>();
240+
241+ if ( mode == ReportMode .GLOBAL_ROOT ) {
242+ // Exclude any class that is not a top-level class
243+ for ( OWLClass unaligned : unalignedClasses ) {
244+ if ( !reasoner .getSuperClasses (unaligned , false ).isTopSingleton () ) {
245+ trimmedOut .add (unaligned );
246+ }
247+ }
248+ } else if ( mode == ReportMode .BASE_ROOT ) {
249+ // Exclude any class that is a descendant of an in-base class
250+ for ( OWLClass unaligned : unalignedClasses ) {
251+ if ( isInBase (unaligned .getIRI ().toString ()) ) {
252+ trimmedOut .addAll (reasoner .getSubClasses (unaligned , false ).getFlattened ());
253+ }
254+ }
255+ }
256+
257+ for ( OWLClass unaligned : unalignedClasses ) {
258+ if ( !trimmedOut .contains (unaligned ) ) {
259+ reportSet .add (unaligned );
260+ }
261+ }
262+
263+ return reportSet ;
264+ }
265+
239266 private boolean isInBase (String iri ) {
240267 for ( String base : basePrefixes ) {
241268 if ( iri .startsWith (base ) ) {
@@ -244,4 +271,20 @@ private boolean isInBase(String iri) {
244271 }
245272 return basePrefixes .isEmpty ();
246273 }
274+
275+ private enum ReportMode {
276+ GLOBAL_ROOT ,
277+ BASE_ROOT ,
278+ ALL ;
279+
280+ static ReportMode fromString (String name ) {
281+ if ( name .equalsIgnoreCase ("all" ) ) {
282+ return ALL ;
283+ } else if ( name .equalsIgnoreCase ("base-root" ) ) {
284+ return BASE_ROOT ;
285+ } else {
286+ return GLOBAL_ROOT ;
287+ }
288+ }
289+ }
247290}
0 commit comments