001/* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 021package org.sonar.channel; 022 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.List; 026 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030public class ChannelDispatcher<OUTPUT> extends Channel<OUTPUT> { 031 032 private static final Logger LOG = LoggerFactory.getLogger(ChannelDispatcher.class); 033 private final boolean failIfNoChannelToConsumeOneCharacter; 034 035 @SuppressWarnings("rawtypes") 036 private final Channel[] channels; 037 038 /** 039 * @deprecated in version 2.9. Please use the builder() method 040 */ 041 @SuppressWarnings("rawtypes") 042 @Deprecated 043 public ChannelDispatcher(List<Channel> channels) { 044 this(channels, false); 045 } 046 047 /** 048 * @deprecated in version 2.9. Please use the builder() method 049 */ 050 @SuppressWarnings("rawtypes") 051 @Deprecated 052 public ChannelDispatcher(Channel... channels) { 053 this(Arrays.asList(channels), false); 054 } 055 056 /** 057 * @deprecated in version 2.9. Please use the builder() method 058 */ 059 @SuppressWarnings("rawtypes") 060 @Deprecated 061 public ChannelDispatcher(List<Channel> channels, boolean failIfNoChannelToConsumeOneCharacter) { 062 this.channels = channels.toArray(new Channel[channels.size()]); 063 this.failIfNoChannelToConsumeOneCharacter = failIfNoChannelToConsumeOneCharacter; 064 } 065 066 private ChannelDispatcher(Builder builder) { 067 this.channels = builder.channels.toArray(new Channel[builder.channels.size()]); 068 this.failIfNoChannelToConsumeOneCharacter = builder.failIfNoChannelToConsumeOneCharacter; 069 } 070 071 @Override 072 public boolean consume(CodeReader code, OUTPUT output) { 073 int nextChar = code.peek(); 074 while (nextChar != -1) { 075 boolean characterConsumed = false; 076 for (Channel<OUTPUT> channel : channels) { 077 if (channel.consume(code, output)) { 078 characterConsumed = true; 079 break; 080 } 081 } 082 if ( !characterConsumed) { 083 if (LOG.isDebugEnabled() || failIfNoChannelToConsumeOneCharacter) { 084 String message = "None of the channel has been able to handle character '" + (char) code.peek() + "' (decimal value " 085 + code.peek() + ") at line " + code.getLinePosition() + ", column " + code.getColumnPosition(); 086 if (failIfNoChannelToConsumeOneCharacter) { 087 throw new IllegalStateException(message); 088 } 089 LOG.debug(message); 090 } 091 code.pop(); 092 } 093 nextChar = code.peek(); 094 } 095 return true; 096 } 097 098 Channel[] getChannels() { 099 return channels; 100 } 101 102 /** 103 * Get a Builder instance to build a new ChannelDispatcher 104 */ 105 public static Builder builder() { 106 return new Builder(); 107 } 108 109 public static final class Builder { 110 111 private List<Channel> channels = new ArrayList<Channel>(); 112 private boolean failIfNoChannelToConsumeOneCharacter = false; 113 114 private Builder() { 115 } 116 117 public Builder addChannel(Channel channel) { 118 channels.add(channel); 119 return this; 120 } 121 122 public Builder addChannels(Channel... c) { 123 for (Channel channel : c) { 124 addChannel(channel); 125 } 126 return this; 127 } 128 129 /** 130 * If this option is activated, an IllegalStateException will be thrown as soon as a character won't be consumed by any channel. 131 */ 132 public Builder failIfNoChannelToConsumeOneCharacter() { 133 failIfNoChannelToConsumeOneCharacter = true; 134 return this; 135 } 136 137 public <OUTPUT> ChannelDispatcher<OUTPUT> build() { 138 return new ChannelDispatcher<OUTPUT>(this); 139 } 140 141 } 142}