001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010-2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2015 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.regex; 019 020import java.io.IOException; 021import java.io.Reader; 022import java.util.AbstractMap; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031/** 032 * Extracts regular expression patterns and/or applied templates from character 033 * streams. If a pattern has a corresponding template, then the template will be 034 * applied to yield the extraction result. If no corresponding template exists, 035 * then the entire match is yielded verbatim. 036 * 037 * @see PatternTemplate 038 */ 039public class StreamPatternExtractor { 040 041 private final Map<String, Pattern> patterns = new LinkedHashMap<>(); 042 043 private final Map<String, PatternTemplate> templates = new HashMap<>(); 044 045 /** 046 * Mapping of names to regular expression patterns to extract from the stream. 047 * @return the patterns' Map keyed with an identifier that may be reused in the templates' Map. 048 */ 049 public Map<String, Pattern> getPatterns() { 050 return patterns; 051 } 052 053 /** 054 * Mapping of names to optional templates to use for yielding pattern match results. 055 * @return the templates' Map keyed with an identifier that has to match a pattern's key. 056 */ 057 public Map<String, PatternTemplate> getTemplates() { 058 return templates; 059 } 060 061 /** 062 * Extracts regular expression patterns from a character streams. Returns a 063 * mapping of names to the results of pattern extraction (literal match or 064 * applied template). 065 * <p> 066 * Patterns are resolved lazily; only as much of the stream is read in order 067 * to satisfy a request for a specific key in the returned map. 068 * <p> 069 * <strong>Note:</strong> If an {@link IOException} is encountered when 070 * accessing the stream, the exception is caught and suppressed. This 071 * results in {@code null} values being returned for values not extracted 072 * before the exception occurred. 073 * 074 * @param reader the character stream . 075 * @return a mapping of names to pattern match results (literal match or 076 * applied template). 077 */ 078 public Iterable<Map.Entry<String, String>> extract(final Reader reader) { 079 return new Iterable<Map.Entry<String, String>>() { 080 private final Map<String, String> values = new HashMap<>(); 081 @SuppressWarnings("rawtypes") 082 private Map.Entry[] entries = patterns.entrySet().toArray( 083 new Map.Entry[patterns.size()]); 084 private final StreamPatternMatches matches = new StreamPatternMatches(reader, patterns 085 .values(), true); 086 087 @Override 088 public Iterator<Map.Entry<String, String>> iterator() { 089 final Iterator<String> iterator = patterns.keySet().iterator(); 090 091 return new Iterator<Map.Entry<String, String>>() { 092 @Override 093 public boolean hasNext() { 094 return iterator.hasNext(); 095 } 096 097 @Override 098 public Entry<String, String> next() { 099 String key = iterator.next(); 100 String value = values.get(key); 101 try { 102 while (value == null && matches.hasNext()) { 103 Matcher matcher = matches.next(); 104 Pattern pattern = matcher.pattern(); 105 for (int n = 0; n < entries.length; n++) { 106 if (entries[n] != null) { 107 String entryKey = (String) (entries[n].getKey()); 108 Pattern entryPattern = (Pattern) (entries[n].getValue()); 109 if (entryPattern == pattern) { 110 // identity equality for accurate (and quick) correlation 111 PatternTemplate t = templates.get(entryKey); 112 String v = 113 (t != null ? t.applyTo(matcher) : matcher 114 .group()); 115 values.put(entryKey, value); 116 if (entryKey.equals(key)) { 117 // found the value we were looking for 118 value = v; 119 } 120 // used entry; deference for efficiency 121 entries[n] = null; 122 } 123 } 124 } 125 } 126 } catch (IOException ioe) { 127 // any failure to read stream yields null value in mapping 128 } 129 return new AbstractMap.SimpleImmutableEntry<>(key, value); 130 } 131 132 @Override 133 public void remove() { 134 throw new UnsupportedOperationException(); 135 } 136 }; 137 } 138 }; 139 } 140}