001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2013 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * SonarQube 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 * SonarQube 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 License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 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<O> extends Channel<O> { 031 032 private static final Logger LOG = LoggerFactory.getLogger(ChannelDispatcher.class); 033 private final boolean failIfNoChannelToConsumeOneCharacter; 034 035 private final Channel<O>[] channels; 036 037 /** 038 * @deprecated in version 2.9. Please use the builder() method 039 */ 040 @SuppressWarnings("rawtypes") 041 @Deprecated 042 public ChannelDispatcher(List<Channel> channels) { 043 this(channels, false); 044 } 045 046 /** 047 * @deprecated in version 2.9. Please use the builder() method 048 */ 049 @SuppressWarnings("rawtypes") 050 @Deprecated 051 public ChannelDispatcher(Channel... channels) { 052 this(Arrays.asList(channels), false); 053 } 054 055 /** 056 * @deprecated in version 2.9. Please use the builder() method 057 */ 058 @SuppressWarnings("rawtypes") 059 @Deprecated 060 public ChannelDispatcher(List<Channel> channels, boolean failIfNoChannelToConsumeOneCharacter) { 061 this.channels = channels.toArray(new Channel[channels.size()]); 062 this.failIfNoChannelToConsumeOneCharacter = failIfNoChannelToConsumeOneCharacter; 063 } 064 065 private ChannelDispatcher(Builder builder) { 066 this.channels = builder.channels.toArray(new Channel[builder.channels.size()]); 067 this.failIfNoChannelToConsumeOneCharacter = builder.failIfNoChannelToConsumeOneCharacter; 068 } 069 070 @Override 071 public boolean consume(CodeReader code, O output) { 072 int nextChar = code.peek(); 073 while (nextChar != -1) { 074 boolean characterConsumed = false; 075 for (Channel<O> 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 <O> ChannelDispatcher<O> build() { 137 return new ChannelDispatcher<O>(this); 138 } 139 140 } 141}