/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.dspl.util; import static com.metsci.glimpse.util.GeneralUtils.newArrayList; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBException; import com.metsci.glimpse.dspl.DsplParser; import com.metsci.glimpse.dspl.schema.Concept; import com.metsci.glimpse.dspl.schema.DataSet; import com.metsci.glimpse.dspl.schema.Slice; import com.metsci.glimpse.dspl.schema.SliceConceptRef; public class DsplSliceUtils { public static Concept getCompatibleMetric( Slice slice, String namespace, String local, boolean nullOnException ) throws JAXBException, IOException, DsplException { // get the dataset file and dspl parser associated with this slice DataSet dataset = slice.getDataSet( ); DsplParser parser = dataset.getParser( ); // get the canonical concept (with its hard-coded namespace and identifier) Concept canonicalConcept = parser.getConcept( namespace, local ); if ( canonicalConcept == null ) { throw new DsplException( "Could not load Metron canonical Concept %s from Namespace %s.", namespace, local ); } // determine if the slice defines a concept which extends canonicalConcept Concept sliceConcept = slice.getCompatibleMetric( canonicalConcept ); if ( sliceConcept == null ) { if ( nullOnException ) return null; else throw new DsplException( "Slice %s does not define a Dimension Concept which extends %s.", slice.getId( ), canonicalConcept.getId( ) ); } return sliceConcept; } public static Concept getCompatibleDimension( Slice slice, String namespace, String local, boolean nullOnException ) throws JAXBException, IOException, DsplException { // get the dataset file and dspl parser associated with this slice DataSet dataset = slice.getDataSet( ); DsplParser parser = dataset.getParser( ); // get the canonical concept (with its hard-coded namespace and identifier) Concept canonicalConcept = parser.getConcept( namespace, local ); if ( canonicalConcept == null ) { throw new DsplException( "Could not load Metron canonical Concept %s from Namespace %s.", namespace, local ); } // determine if the slice defines a concept which extends canonicalConcept Concept sliceConcept = slice.getCompatibleDimension( canonicalConcept ); if ( sliceConcept == null ) { if ( nullOnException ) return null; else throw new DsplException( "Slice %s does not define a Dimension Concept which extends %s.", slice.getId( ), canonicalConcept.getId( ) ); } return sliceConcept; } public static abstract class SlicePattern { public abstract boolean matches( Slice slice ) throws JAXBException, IOException, DsplException; public List<Slice> find( DataSet dataset ) throws JAXBException, IOException, DsplException { List<Slice> compatibleSlices = newArrayList( ); if ( dataset.getSlices( ) != null ) for ( Slice slice : dataset.getSlices( ).getSlice( ) ) if ( matches( slice ) ) compatibleSlices.add( slice ); return compatibleSlices; } } public static class SimpleSlicePattern extends SlicePattern { private final List<ConceptPattern> dimensionPatterns; private final List<ConceptPattern> metricPatterns; public SimpleSlicePattern( List<ConceptPattern> dimensionPatterns, List<ConceptPattern> metricPatterns ) { this.dimensionPatterns = new ArrayList<ConceptPattern>( dimensionPatterns ); this.metricPatterns = new ArrayList<ConceptPattern>( metricPatterns ); } @Override public boolean matches( Slice slice ) throws JAXBException, IOException, DsplException { // check for one and only one compatible dimension for each dimension pattern for ( ConceptPattern pattern : dimensionPatterns ) { List<Concept> matches = pattern.findDimensions( slice ); if ( matches.size( ) != 1 ) return false; } // check for one and only one compatible metric for each metric pattern for ( ConceptPattern pattern : metricPatterns ) { List<Concept> matches = pattern.findMetrics( slice ); if ( matches.size( ) != 1 ) return false; } return true; } } public static abstract class ConceptPattern { public abstract boolean matches( Concept concept ); public List<Concept> findDimensions( Slice slice ) throws JAXBException, IOException, DsplException { List<Concept> matches = newArrayList( ); List<SliceConceptRef> refs = slice.getDimension( ); for ( SliceConceptRef ref : refs ) if ( matches( ref.getConcept( ) ) ) matches.add( ref.getConcept( ) ); return matches; } public List<Concept> findMetrics( Slice slice ) throws JAXBException, IOException, DsplException { List<Concept> matches = newArrayList( ); List<SliceConceptRef> refs = slice.getMetric( ); for ( SliceConceptRef ref : refs ) if ( matches( ref.getConcept( ) ) ) matches.add( ref.getConcept( ) ); return matches; } public Concept findDimension( Slice slice ) throws JAXBException, IOException, DsplException { List<Concept> matches = findDimensions( slice ); if ( matches.size( ) != 1 ) return null; return matches.get( 0 ); } public Concept findMetric( Slice slice ) throws JAXBException, IOException, DsplException { List<Concept> matches = findMetrics( slice ); if ( matches.size( ) != 1 ) return null; return matches.get( 0 ); } } public static class SimpleConceptPattern extends ConceptPattern { public final String namespace; public final String localId; public SimpleConceptPattern( Concept concept ) { this.namespace = concept.getDataSet( ).getTargetNamespace( ); this.localId = concept.getId( ); } public SimpleConceptPattern( String namespace, String localId ) { this.namespace = namespace; this.localId = localId; } @Override public boolean matches( Concept concept ) { if ( idMatches( concept ) ) return true; Concept parentConcept = concept; while ( ( parentConcept = parentConcept.getParentConcept( ) ) != null ) if ( idMatches( parentConcept ) ) return true; return false; } private boolean idMatches( Concept concept ) { boolean namespaceMatch = concept.getDataSet( ).getTargetNamespace( ).contentEquals( namespace ); boolean idMatch = concept.getId( ).contentEquals( localId ); return namespaceMatch && idMatch; } @Override public String toString( ) { return "[ namespace = " + namespace + ", localId = " + localId + "]"; } } }