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
021 package org.sonar.channel;
022
023 import java.util.ArrayList;
024 import java.util.Arrays;
025 import java.util.List;
026
027 import org.slf4j.Logger;
028 import org.slf4j.LoggerFactory;
029
030 public 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 public boolean consume(CodeReader code, OUTPUT output) {
072 int nextChar = code.peek();
073 while (nextChar != -1) {
074 boolean characterConsumed = false;
075 for (Channel<OUTPUT> channel : channels) {
076 if (channel.consume(code, output)) {
077 characterConsumed = true;
078 break;
079 }
080 }
081 if ( !characterConsumed) {
082 if (LOG.isDebugEnabled() || failIfNoChannelToConsumeOneCharacter) {
083 String message = "None of the channel has been able to handle character '" + (char) code.peek() + "' (decimal value "
084 + code.peek() + ") at line " + code.getLinePosition() + ", column " + code.getColumnPosition();
085 if (failIfNoChannelToConsumeOneCharacter) {
086 throw new IllegalStateException(message);
087 }
088 LOG.debug(message);
089 }
090 code.pop();
091 }
092 nextChar = code.peek();
093 }
094 return true;
095 }
096
097 Channel[] getChannels() {
098 return channels;
099 }
100
101 /**
102 * Get a Builder instance to build a new ChannelDispatcher
103 */
104 public static Builder builder() {
105 return new Builder();
106 }
107
108 public static final class Builder {
109
110 private List<Channel> channels = new ArrayList<Channel>();
111 private boolean failIfNoChannelToConsumeOneCharacter = false;
112
113 private Builder() {
114 }
115
116 public Builder addChannel(Channel channel) {
117 channels.add(channel);
118 return this;
119 }
120
121 public Builder addChannels(Channel... c) {
122 for (Channel channel : c) {
123 addChannel(channel);
124 }
125 return this;
126 }
127
128 /**
129 * If this option is activated, an IllegalStateException will be thrown as soon as a character won't be consumed by any channel.
130 */
131 public Builder failIfNoChannelToConsumeOneCharacter() {
132 failIfNoChannelToConsumeOneCharacter = true;
133 return this;
134 }
135
136 public <OUTPUT> ChannelDispatcher<OUTPUT> build() {
137 return new ChannelDispatcher<OUTPUT>(this);
138 }
139
140 }
141 }