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 }